/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BrushStroke = Container.expand(function (color, size) { var self = Container.call(this); var stroke = self.attachAsset('brushStroke', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 30, scaleY: size / 30 }); stroke.tint = color; return self; }); var Button = Container.expand(function (label, color) { var self = Container.call(this); var background = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5 }); var text = new Text2(label, { size: 30, fill: 0xFFFFFF }); text.anchor.set(0.5, 0.5); self.addChild(text); if (color !== undefined) { background.tint = color; } self.down = function () { tween(background, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; self.up = function () { tween(background, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var ColorButton = Container.expand(function (color) { var self = Container.call(this); var button = self.attachAsset('colorButton', { anchorX: 0.5, anchorY: 0.5 }); button.tint = color; self.color = color; self.down = function () { tween(button, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; self.up = function () { tween(button, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var ColorPalette = Container.expand(function () { var self = Container.call(this); var background = LK.getAsset('colorPanel', { anchorX: 0.5, anchorY: 0.5, scaleX: 1881 / 1880, // Set to exact size 1881 scaleY: 1899 / 277 // Set to exact size 1899 }); self.addChild(background); var colorButtons = []; // Generate 256 color gradient palette var paletteColors = []; var buttonSize = 90; // Increased from 30 to 90 (3x larger) var spacing = 12; // Increased from 4 to 12 (3x larger) var columns = 16; var rows = 16; // Generate rainbow gradient colors for the full 256 color palette for (var i = 0; i < 256; i++) { var hue = i / 256; var r, g, b; // Convert HSV to RGB (simplified rainbow gradient) if (hue < 1 / 6) { r = 1; g = hue * 6; b = 0; } else if (hue < 2 / 6) { r = (2 / 6 - hue) * 6; g = 1; b = 0; } else if (hue < 3 / 6) { r = 0; g = 1; b = (hue - 2 / 6) * 6; } else if (hue < 4 / 6) { r = 0; g = (4 / 6 - hue) * 6; b = 1; } else if (hue < 5 / 6) { r = (hue - 4 / 6) * 6; b = 1; g = 0; } else { r = 1; g = 0; b = (1 - hue) * 6; } // Convert to 0-255 range var red = Math.floor(r * 255); var green = Math.floor(g * 255); var blue = Math.floor(b * 255); var color = red << 16 | green << 8 | blue; paletteColors.push(color); } for (var i = 0; i < paletteColors.length; i++) { var row = Math.floor(i / columns); var col = i % columns; var colorBtn = new ColorButton(paletteColors[i]); colorBtn.x = col * (buttonSize + spacing) - (columns - 1) * (buttonSize + spacing) / 2; colorBtn.y = row * (buttonSize + spacing) - (rows - 1) * (buttonSize + spacing) / 2; colorBtn.scaleX = 4.5; // Increased from 1.5 to 4.5 (3x larger) colorBtn.scaleY = 4.5; // Increased from 1.5 to 4.5 (3x larger) self.addChild(colorBtn); // Create a closure to capture the current color (function (color, btn) { btn.down = function () { currentColor = color; self.visible = false; // Notify the game that a color was selected if (self.onColorSelected) { self.onColorSelected(color); } }; })(paletteColors[i], colorBtn); colorButtons.push(colorBtn); } // Add selector ring var selector = LK.getAsset('brushStroke', { anchorX: 0.5, anchorY: 0.5, scaleX: 7.5, // Increased from 2.5 to 7.5 (3x larger) scaleY: 7.5 // Increased from 2.5 to 7.5 (3x larger) }); selector.tint = 0xFFFFFF; selector.alpha = 0.7; self.addChild(selector); // Set selector initial position selector.x = colorButtons[0].x; selector.y = colorButtons[0].y; // Hide palette initially self.visible = false; // Method to update selector position self.updateSelector = function (color) { for (var i = 0; i < paletteColors.length; i++) { if (paletteColors[i] === color) { selector.x = colorButtons[i].x; selector.y = colorButtons[i].y; break; } } }; return self; }); var CompletedEgg = Container.expand(function (baseColor) { var self = Container.call(this); var eggGraphic = self.attachAsset('completedEgg', { anchorX: 0.5, anchorY: 0.5 }); eggGraphic.tint = baseColor || 0xFFFFFF; // Add some random decoration to make each egg look unique for (var i = 0; i < 5; i++) { var glitter = self.attachAsset('glitter', { anchorX: 0.5, anchorY: 0.5, x: (Math.random() - 0.5) * 80, y: (Math.random() - 0.5) * 120 }); glitter.tint = 0xFFD700 + Math.floor(Math.random() * 0x555555); } return self; }); var EasterEgg = Container.expand(function () { var self = Container.call(this); var eggBase = self.attachAsset('eggBase', { anchorX: 0.5, anchorY: 0.5 }); var designContainer = new Container(); self.designContainer = designContainer; // Make accessible outside self.addChild(designContainer); self.addDesign = function (x, y, designElement) { var localPosition = designContainer.toLocal({ x: x, y: y }, game); // Check if the point is within the bounds of the eggBase // Convert to egg's local coordinates for hit testing var eggLocalPosition = self.toLocal({ x: x, y: y }, game); // Calculate distance from center of egg var dx = eggLocalPosition.x; var dy = eggLocalPosition.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if point is inside the egg (ellipse) // Using the egg's dimensions to determine the ellipse bounds var eggWidth = eggBase.width / 2; var eggHeight = eggBase.height / 2; // Formula for point in ellipse: (x/a)² + (y/b)² <= 1 // where a is half-width and b is half-height var isInEgg = dx * dx / (eggWidth * eggWidth) + dy * dy / (eggHeight * eggHeight) <= 1; // Only add design if point is inside the egg if (isInEgg) { designElement.x = localPosition.x; designElement.y = localPosition.y; designContainer.addChild(designElement); // Add to design history for undo functionality designHistory.push({ element: designElement, index: designContainer.children.length - 1 }); } }; self.clearDesigns = function () { while (designContainer.children.length > 0) { designContainer.removeChild(designContainer.children[0]); } }; self.rotate = function (angle) { eggBase.rotation += angle; designContainer.rotation += angle; }; self.captureDesign = function () { // In a real implementation, this would create a snapshot of the egg // For this simplified version, we just create a new completed egg with the same tint var completedEgg = new CompletedEgg(eggBase.tint); return completedEgg; }; return self; }); var Eraser = Container.expand(function (size) { var self = Container.call(this); // Create a visual representation of the eraser var eraserVisual = self.attachAsset('brushStroke', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 30, scaleY: size / 30 }); // Make it white with a border eraserVisual.tint = 0xFFFFFF; eraserVisual.alpha = 0.7; return self; }); var Slider = Container.expand(function (minVal, maxVal, defaultVal, width, color) { var self = Container.call(this); // Track properties self.minValue = minVal || 0; self.maxValue = maxVal || 100; self.value = defaultVal || 50; self.trackWidth = width || 300; self.trackColor = color || 0xCCCCCC; // Create track var track = LK.getAsset('buttonBg', { anchorX: 0, anchorY: 0.5, scaleX: self.trackWidth / 120, scaleY: 0.3 }); track.tint = self.trackColor; self.addChild(track); self.track = track; // Store track reference for external access // Create handle var handle = LK.getAsset('colorButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); handle.tint = 0xFFFFFF; handle.x = (self.value - self.minValue) / (self.maxValue - self.minValue) * self.trackWidth; self.addChild(handle); self.handle = handle; // No value label is needed // Method definitions // Convert value to x position self.getXForValue = function (val) { return (val - self.minValue) / (self.maxValue - self.minValue) * self.trackWidth; }; // Convert x position to value self.getValueForX = function (x) { return self.minValue + x / self.trackWidth * (self.maxValue - self.minValue); }; // Update handle position self.updateHandlePosition = function () { handle.x = self.getXForValue(self.value); }; // Set value self.setValue = function (val) { self.value = Math.max(self.minValue, Math.min(self.maxValue, val)); self.updateHandlePosition(); if (self.onValueChanged) { self.onValueChanged(self.value); } }; // Handle drag self.down = function (x, y) { self.isDragging = true; self.setValue(self.getValueForX(x)); }; self.move = function (x, y) { if (self.isDragging) { self.setValue(self.getValueForX(Math.max(0, Math.min(self.trackWidth, x)))); } }; self.up = function () { self.isDragging = false; }; return self; }); var Sticker = Container.expand(function (type, scale) { var self = Container.call(this); var stickerAsset; if (type === 'star') { stickerAsset = self.attachAsset('stickerStar', { anchorX: 0.5, anchorY: 0.5, scaleX: scale * 5, scaleY: scale * 5 }); } else if (type === 'heart') { stickerAsset = self.attachAsset('stickerHeart', { anchorX: 0.5, anchorY: 0.5, scaleX: scale * 5, scaleY: scale * 5 }); } self.down = function () { tween(stickerAsset, { scaleX: scale * 5 * 0.9, scaleY: scale * 5 * 0.9 }, { duration: 100 }); }; self.up = function () { tween(stickerAsset, { scaleX: scale * 5, scaleY: scale * 5 }, { duration: 100 }); }; return self; }); var Watermark = Container.expand(function (text, opacity) { var self = Container.call(this); // Set default values if not provided self.text = text || "FRVR Easter"; self.opacity = opacity || 0.35; // Create corner text elements function createCornerText(x, y, rotation) { var cornerText = new Text2(self.text, { size: 28, fill: 0x333333 }); cornerText.anchor.set(0.5, 0.5); cornerText.x = x; cornerText.y = y; cornerText.rotation = rotation || 0; cornerText.alpha = self.opacity; self.addChild(cornerText); return cornerText; } // Create text for each corner, avoiding top-left 100x100 area self.topRight = createCornerText(2048 - 120, 120, Math.PI / 4); self.bottomRight = createCornerText(2048 - 120, 2732 - 120, -Math.PI / 4); self.bottomLeft = createCornerText(120, 2732 - 120, Math.PI / 4); // Top left is placed with extra margin to avoid the platform menu icon self.topLeft = createCornerText(180, 180, -Math.PI / 4); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xAFEEEE // Light turquoise background color }); /**** * Game Code ****/ // Define color palette - arranged in rainbow gradient var colors = [ // Black (left) 0x000000, // Violet group 0x9400D3, // Violet 0x4B0082, // Indigo 0x8A2BE2, // Blue Violet // Blue group 0x0000FF, // Blue 0x000080, // Navy Blue 0x00FFFF, // Cyan 0x008080, // Teal // Green group 0x00FF00, // Green 0x32CD32, // Lime Green 0x808000, // Olive // Red group 0x800000, // Maroon 0xA52A2A, // Brown 0xFF0000, // Red 0xFF7F00, // Orange // Yellow group 0xFFD700, // Gold 0xFFFF00, // Yellow // Pink group 0xFF69B4, // Hot Pink // Gray 0x808080, // White (right) 0xFFFFFF]; // Define brush sizes var brushSizes = [10, 20, 30]; // Initialize game variables var currentColor = colors[0]; var currentBrushSize = brushSizes[1]; var currentEraserSize = 50; var currentStickerSize = 1.2; var currentTool = 'brush'; var previousTool = null; var isPainting = false; var lastX, lastY; var selectedColorButton = null; // Keep track of actions for undo functionality var designHistory = []; // Initialize empty savedEggs array var savedEggs = []; // Basket already created earlier // Create main egg var easterEgg = new EasterEgg(); easterEgg.x = 2048 / 2; easterEgg.y = 2732 / 2 - 200; game.addChild(easterEgg); // Create UI components // Tool panel var toolPanel = LK.getAsset('toolPanel', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 5, y: 2732 - 100 // Moved up by 20 units }); game.addChild(toolPanel); // Color panel var colorPanel = LK.getAsset('colorPanel', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 - 345 // Moved down by 5 units }); game.addChild(colorPanel); // Add color buttons var colorButtons = []; for (var i = 0; i < colors.length; i++) { var colorButton = new ColorButton(colors[i]); // Position in two rows (10 in first row, 10 in second row) if (i < 10) { // First row colorButton.x = i * 130 + 160; // Changed spacing from 145 to 130, moved left by 10 units colorButton.y = 2732 - 410; // First row - moved up by 10 units } else { // Second row colorButton.x = (i - 10) * 130 + 160; // Changed spacing from 145 to 130, moved left by 10 units colorButton.y = 2732 - 280; // Second row } game.addChild(colorButton); colorButtons.push(colorButton); // Scale up by 2x tween(colorButton, { scaleX: 2, scaleY: 2 }, { duration: 300 }); // Add click handler (function (color, button) { colorButton.down = function () { tween(colorButton, { scaleX: 1.8, scaleY: 1.8 }, { duration: 100 }); currentColor = color; }; colorButton.up = function () { tween(colorButton, { scaleX: 2, scaleY: 2 }, { duration: 100 }); // Stop any animations on previously selected button if (selectedColorButton) { tween.stop(selectedColorButton); tween(selectedColorButton, { scaleX: 2, scaleY: 2 }, { duration: 100 }); } // Set new selected button selectedColorButton = button; // Start pulsing animation function pulseButton() { tween(selectedColorButton, { scaleX: 2.2, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(selectedColorButton, { scaleX: 1.8, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: pulseButton }); } }); } // Start the pulsing animation pulseButton(); }; })(colors[i], colorButton); } // Create tool buttons // Calculate button width including scale factor var buttonWidth = 120 * 2; // 120px width * 2 scale var buttonSpacing = 25; // 25 units apart var startX = 300; // Starting X position // Create tool buttons var brushButton = new Button("Brush", 0x6495ED); brushButton.x = startX - 75; // Moved left by 25 more units brushButton.y = 2732 - 100; // Moved up by 20 units game.addChild(brushButton); tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); brushButton.down = function () { tween(brushButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; brushButton.up = function () { tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Hide any previous tool's sliders first eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Show size slider when brush tool is selected sizeSlider.visible = true; sizeLabel.visible = true; // Update slider track color to match button sizeSlider.trackColor = 0x6495ED; // Match brush button color sizeSlider.track.tint = 0x6495ED; // Store previous tool and set current tool to brush previousTool = currentTool; currentTool = 'brush'; // Stop any animations on buttons tween.stop(brushButton); tween.stop(stickerStarButton); tween.stop(stickerHeartButton); tween.stop(eraserButton); tween.stop(rotateLeftButton); tween.stop(rotateRightButton); tween.stop(saveButton); tween.stop(clearButton); // Reset all buttons to normal size tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(saveButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Start pulsing animation for brush button function pulseBrushButton() { tween(brushButton, { scaleX: 2.4, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushButton, { scaleX: 2.0, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: pulseBrushButton }); } }); } // Start the pulsing animation pulseBrushButton(); }; var stickerStarButton = new Button("Star", 0xFFD700); stickerStarButton.x = startX + (buttonWidth + buttonSpacing) * 1 - 75; // Moved left by 25 more units stickerStarButton.y = 2732 - 100; // Moved up by 20 units game.addChild(stickerStarButton); // Set background to gold color stickerStarButton.children[0].tint = 0xFFD700; // Gold color tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); stickerStarButton.down = function () { tween(stickerStarButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; stickerStarButton.up = function () { tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Store previous tool and set current tool to star previousTool = currentTool; currentTool = 'star'; // First hide any previously visible sliders eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Show size slider for star (not toggle) sizeSlider.visible = true; // Update slider track color to match button sizeSlider.trackColor = 0xFFD700; // Gold color to match button sizeSlider.track.tint = 0xFFD700; // Stop any animations on buttons tween.stop(brushButton); tween.stop(stickerStarButton); tween.stop(stickerHeartButton); tween.stop(eraserButton); tween.stop(rotateLeftButton); tween.stop(rotateRightButton); tween.stop(saveButton); tween.stop(clearButton); // Reset all buttons to normal size tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(saveButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Start pulsing animation for star button function pulseStarButton() { tween(stickerStarButton, { scaleX: 2.4, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(stickerStarButton, { scaleX: 2.0, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: pulseStarButton }); } }); } // Start the pulsing animation pulseStarButton(); }; var stickerHeartButton = new Button("Heart", 0xFF0000); stickerHeartButton.x = startX + (buttonWidth + buttonSpacing) * 2 - 75; // Moved left by 25 more units stickerHeartButton.y = 2732 - 100; // Moved up by 20 units game.addChild(stickerHeartButton); tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); stickerHeartButton.down = function () { tween(stickerHeartButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; stickerHeartButton.up = function () { tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Store previous tool and set current tool to heart previousTool = currentTool; currentTool = 'heart'; // First hide any previously visible sliders eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Show size slider for heart (not toggle) sizeSlider.visible = true; // Update slider track color to match button sizeSlider.trackColor = 0xFF0000; // Red color to match button sizeSlider.track.tint = 0xFF0000; // Keep handle white for better visibility // Stop any animations on buttons tween.stop(brushButton); tween.stop(stickerStarButton); tween.stop(stickerHeartButton); tween.stop(eraserButton); tween.stop(rotateLeftButton); tween.stop(rotateRightButton); tween.stop(saveButton); tween.stop(clearButton); // Reset all buttons to normal size tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(saveButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Start pulsing animation for heart button function pulseHeartButton() { tween(stickerHeartButton, { scaleX: 2.4, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(stickerHeartButton, { scaleX: 2.0, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: pulseHeartButton }); } }); } // Start the pulsing animation pulseHeartButton(); }; var rotateLeftButton = new Button("< Rotate", 0x9370DB); rotateLeftButton.x = startX + (buttonWidth + buttonSpacing) * 3 - 75; // Moved left by 25 more units rotateLeftButton.y = 2732 - 100; // Moved up by 20 units game.addChild(rotateLeftButton); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); rotateLeftButton.down = function () { tween(rotateLeftButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; rotateLeftButton.up = function () { tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Hide any open sliders sizeSlider.visible = false; eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Store previous tool before rotation previousTool = currentTool; easterEgg.rotate(-Math.PI / 8); }; // Add a thin grey vertical divider between rotate buttons var divider = LK.getAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, x: startX + (buttonWidth + buttonSpacing) * 3.5 - 75, // Position between the two rotate buttons y: 2732 - 100, // Same Y as buttons scaleX: 0.05, // Make it thin scaleY: 2 // Same height as buttons }); divider.tint = 0xe6e6fa; // Light lavender color game.addChild(divider); var rotateRightButton = new Button("Rotate >", 0x9370DB); rotateRightButton.x = startX + (buttonWidth + buttonSpacing) * 4 - 75; // Moved left by 25 more units rotateRightButton.y = 2732 - 100; // Moved up by 20 units game.addChild(rotateRightButton); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); rotateRightButton.down = function () { tween(rotateRightButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; rotateRightButton.up = function () { tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Hide any open sliders sizeSlider.visible = false; eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Store previous tool before rotation previousTool = currentTool; easterEgg.rotate(Math.PI / 8); }; var saveButton = new Button("Undo", 0x8B4513); saveButton.x = startX + (buttonWidth + buttonSpacing) * 5 - 75; // Moved left by 25 more units saveButton.y = 2732 - 100; // Moved up by 20 units game.addChild(saveButton); tween(saveButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); saveButton.down = function () { tween(saveButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); // Start continuous undo functionality var performUndo = function performUndo() { if (designHistory.length > 0) { var lastDesign = designHistory.pop(); // Handle clear operation undo (restore all elements that were cleared) if (lastDesign.clearOperation) { // First clear any current elements to avoid duplicates easterEgg.clearDesigns(); // Add back all elements that were cleared for (var i = 0; i < lastDesign.elements.length; i++) { easterEgg.designContainer.addChild(lastDesign.elements[i]); } LK.getSound('saveSound').play(); } // Handle erased element undo (add back erased element) else if (lastDesign.erased) { easterEgg.designContainer.addChildAt(lastDesign.element, lastDesign.index); LK.getSound('saveSound').play(); } // Handle regular element undo (remove added element) else if (easterEgg.designContainer && easterEgg.designContainer.children.length > 0) { // Check if the element is still at the expected index if (easterEgg.designContainer.children[lastDesign.index] === lastDesign.element) { easterEgg.designContainer.removeChild(lastDesign.element); } else { // If not at expected index, find and remove it by reference for (var i = 0; i < easterEgg.designContainer.children.length; i++) { if (easterEgg.designContainer.children[i] === lastDesign.element) { easterEgg.designContainer.removeChild(lastDesign.element); break; } } } LK.getSound('saveSound').play(); } } }; // Perform first undo immediately performUndo(); // Setup interval for continuous undo while button is pressed saveButton.undoInterval = LK.setInterval(function () { performUndo(); }, 150); // Undo every 150ms while pressed (2x faster) }; saveButton.up = function () { tween(saveButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Hide any previous tool's sliders first sizeSlider.visible = false; eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Stop continuous undo when releasing the button if (saveButton.undoInterval) { LK.clearInterval(saveButton.undoInterval); saveButton.undoInterval = null; } // Deactivate previous tool and activate save tool if (currentTool !== 'save') { var _pulseSaveButton = function pulseSaveButton() { tween(saveButton, { scaleX: 2.4, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(saveButton, { scaleX: 2.0, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: _pulseSaveButton }); } }); }; // Start the pulsing animation // Stop any animations on buttons tween.stop(brushButton); tween.stop(stickerStarButton); tween.stop(stickerHeartButton); tween.stop(eraserButton); tween.stop(rotateLeftButton); tween.stop(rotateRightButton); tween.stop(saveButton); tween.stop(clearButton); // Reset all buttons to normal size tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Store previous tool and set current tool to save previousTool = currentTool; currentTool = 'save'; // Start pulsing animation for save button _pulseSaveButton(); } }; var eraserButton = new Button("Eraser", 0x808080); eraserButton.x = colorPanel.x + colorPanel.width / 2 - 145; // Moved right by 100 units eraserButton.y = colorPanel.y - colorPanel.height / 2 + 139; // Moved down to match all colors button game.addChild(eraserButton); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 300 }); eraserButton.down = function () { tween(eraserButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; eraserButton.up = function () { tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); // Toggle eraser mode if (currentTool === 'eraser') { var _pulseBrushButton = function pulseBrushButton() { tween(brushButton, { scaleX: 2.4, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(brushButton, { scaleX: 2.0, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: _pulseBrushButton }); } }); }; // Store previous tool and switch back to brush previousTool = currentTool; currentTool = 'brush'; // Stop animation on eraser button tween.stop(eraserButton); tween(eraserButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); // Hide eraser size slider eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Start pulsing animation for brush button _pulseBrushButton(); } else { var _pulseEraserButton = function pulseEraserButton() { tween(eraserButton, { scaleX: 2.4, scaleY: 3.3 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(eraserButton, { scaleX: 2.0, scaleY: 2.9 }, { duration: 500, easing: tween.easeInOut, onFinish: _pulseEraserButton }); } }); }; // Store previous tool and switch to eraser mode previousTool = currentTool; currentTool = 'eraser'; // Stop animations on all other buttons tween.stop(brushButton); tween.stop(stickerStarButton); tween.stop(stickerHeartButton); tween.stop(rotateLeftButton); tween.stop(rotateRightButton); tween.stop(clearButton); // Reset all buttons to normal size tween(brushButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerStarButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(stickerHeartButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateLeftButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(rotateRightButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // First hide any previously visible sliders sizeSlider.visible = false; // Show eraser size slider eraserSizeSlider.visible = true; eraserSizeLabel.visible = true; // Start pulsing animation for eraser button _pulseEraserButton(); } }; // Add color palette button var allColorButton = new Button("All Colors", 0x9370DB); allColorButton.x = colorPanel.x + colorPanel.width / 2 - 415; // Position it - moved left by 100 units (from -315 to -415) allColorButton.y = colorPanel.y - colorPanel.height / 2 + 139; // Moved down by 4 units (from 135 to 139) game.addChild(allColorButton); tween(allColorButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 300 }); // Apply rainbow gradient to all colors button var btnBackground = allColorButton.children[0]; var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3]; var colorIndex = 0; var duration = 3000; // 3 seconds for full rainbow cycle // Function to cycle through rainbow colors function cycleRainbowColors() { tween(btnBackground, { tint: rainbowColors[colorIndex] }, { duration: duration / rainbowColors.length, onFinish: function onFinish() { colorIndex = (colorIndex + 1) % rainbowColors.length; cycleRainbowColors(); } }); } // Start the rainbow cycle cycleRainbowColors(); allColorButton.down = function () { tween(allColorButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; allColorButton.up = function () { // Reset to normal size first tween(allColorButton, { scaleX: 2.2, scaleY: 3.1 }, { duration: 100 }); // Toggle the full color palette colorPalette.visible = !colorPalette.visible; // Update selector position to match current selected color if (colorPalette.visible) { colorPalette.updateSelector(currentColor); } }; var clearButton = new Button("Clear", 0x444444); clearButton.x = startX + (buttonWidth + buttonSpacing) * 6 - 75; // Moved next to save button clearButton.y = 2732 - 100; // Moved up by 20 units game.addChild(clearButton); tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 300 }); clearButton.down = function () { tween(clearButton, { scaleX: 1.98, scaleY: 1.8 }, { duration: 100 }); }; clearButton.up = function () { tween(clearButton, { scaleX: 2.2, scaleY: 2 }, { duration: 100 }); // Hide any open sliders sizeSlider.visible = false; eraserSizeSlider.visible = false; eraserSizeLabel.visible = false; // Store previous tool before clearing previousTool = currentTool; // Save the current design state for potential undo if (easterEgg.designContainer && easterEgg.designContainer.children.length > 0) { // Create a special history entry for the clear operation designHistory.push({ clearOperation: true, elements: easterEgg.designContainer.children.slice() // Make a copy of all elements }); } easterEgg.clearDesigns(); LK.getSound('clearSound').play(); }; // Function to toggle eraser tool function toggleEraserThicknessSlider(visible) { // Set eraser as current tool when enabled if (visible) { currentTool = 'eraser'; } } // Create basket display var basket = LK.getAsset('basket', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 - 525 // Moved up by 25 units (from 500 to 525) }); game.addChildAt(basket, 0); // Add at index 0 to ensure it's behind the egg // Bring the easterEgg to the front to ensure proper display order game.removeChild(easterEgg); game.addChild(easterEgg); // Title text var titleText = new Text2("Easter Egg Artist", { size: 100, fill: 0x8B4513 // Dark brown }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 50; game.addChild(titleText); // Score display - hidden but kept in code for reference var scoreText = new Text2("Eggs: 0", { size: 60, fill: 0x4B0082 // Indigo }); scoreText.anchor.set(0, 0); scoreText.x = 120; scoreText.y = 120; scoreText.visible = false; // Hide the score text // Function to display saved eggs (placeholder for new implementation) function displaySavedEggs() { // Remove any existing displayed eggs for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i].isCompletedEgg) { game.removeChild(game.children[i]); } } // Display saved eggs (will be reimplemented) for (var i = 0; i < savedEggs.length; i++) { var eggData = savedEggs[i]; var displayedEgg = new CompletedEgg(eggData.color); displayedEgg.x = eggData.x; displayedEgg.y = eggData.y; displayedEgg.isCompletedEgg = true; game.addChild(displayedEgg); } } // Display any saved eggs displaySavedEggs(); // Game event handlers game.down = function (x, y) { if (y < 2732 - 350) { isPainting = true; lastX = x; lastY = y; if (currentTool === 'brush') { var brushStroke = new BrushStroke(currentColor, currentBrushSize); easterEgg.addDesign(x, y, brushStroke); LK.getSound('brushSound').play(); } else if (currentTool === 'star' || currentTool === 'heart') { var size = currentStickerSize; var sticker = new Sticker(currentTool, size); easterEgg.addDesign(x, y, sticker); LK.getSound('stickerSound').play(); } else if (currentTool === 'eraser') { // Create a visual eraser element but don't actually add it to the design var eraser = new Eraser(eraserSizeSlider.value); // Store reference to enable updating size from slider game.eraser = eraser; // Convert to egg's local coordinates for hit testing var eggLocalPosition = easterEgg.toLocal({ x: x, y: y }, game); // Check if point is inside egg boundary var eggBase = easterEgg.children[0]; // The eggBase is the first child var eggWidth = eggBase.width / 2; var eggHeight = eggBase.height / 2; var dx = eggLocalPosition.x; var dy = eggLocalPosition.y; var isInEgg = dx * dx / (eggWidth * eggWidth) + dy * dy / (eggHeight * eggHeight) <= 1; if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) { // Find and remove elements near the eraser position for (var i = easterEgg.designContainer.children.length - 1; i >= 0; i--) { var element = easterEgg.designContainer.children[i]; var distance = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2)); // If element is within eraser size, remove it if (distance < eraserSizeSlider.value / 2) { // Save the removed element to history for potential undo designHistory.push({ element: element, index: i, erased: true // Mark as erased rather than added }); easterEgg.designContainer.removeChild(element); LK.getSound('brushSound').play(); break; // Remove one element at a time for better control } } } } } }; game.move = function (x, y) { if (isPainting && (currentTool === 'brush' || currentTool === 'eraser')) { // Calculate distance between last point and current point var dx = x - lastX; var dy = y - lastY; var distance = Math.sqrt(dx * dx + dy * dy); // Get current size based on tool var currentSize = currentTool === 'brush' ? currentBrushSize : currentEraserSize; // Get current color based on tool var strokeColor = currentTool === 'brush' ? currentColor : 0xFFFFFF; // Play brush sound occasionally while using eraser if (currentTool === 'eraser' && Math.random() < 0.05) { LK.getSound('brushSound').play(); } // If distance is greater than half the size, add intermediate points if (distance > currentSize / 2) { var steps = Math.ceil(distance / (currentSize / 2)); for (var i = 1; i <= steps; i++) { var pointX = lastX + dx * i / steps; var pointY = lastY + dy * i / steps; if (currentTool === 'eraser') { // Calculate egg local position for eraser var eggLocalPosition = easterEgg.toLocal({ x: pointX, y: pointY }, game); // Check if point is inside egg boundary var eggBase = easterEgg.children[0]; var eggWidth = eggBase.width / 2; var eggHeight = eggBase.height / 2; var localDx = eggLocalPosition.x; var localDy = eggLocalPosition.y; var isInEgg = localDx * localDx / (eggWidth * eggWidth) + localDy * localDy / (eggHeight * eggHeight) <= 1; if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) { // Find and remove elements near the eraser position for (var j = easterEgg.designContainer.children.length - 1; j >= 0; j--) { var element = easterEgg.designContainer.children[j]; var eraseDist = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2)); // If element is within eraser size, remove it if (eraseDist < eraserSizeSlider.value / 2) { // Save the removed element to history for potential undo designHistory.push({ element: element, index: j, erased: true // Mark as erased rather than added }); easterEgg.designContainer.removeChild(element); break; // Remove one element at a time for better control } } } } else { var stroke = new BrushStroke(strokeColor, currentSize); easterEgg.addDesign(pointX, pointY, stroke); } } } else { if (currentTool === 'eraser') { // Calculate egg local position for eraser var eggLocalPosition = easterEgg.toLocal({ x: x, y: y }, game); // Check if point is inside egg boundary var eggBase = easterEgg.children[0]; var eggWidth = eggBase.width / 2; var eggHeight = eggBase.height / 2; var localDx = eggLocalPosition.x; var localDy = eggLocalPosition.y; var isInEgg = localDx * localDx / (eggWidth * eggWidth) + localDy * localDy / (eggHeight * eggHeight) <= 1; if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) { // Find and remove elements near the eraser position for (var j = easterEgg.designContainer.children.length - 1; j >= 0; j--) { var element = easterEgg.designContainer.children[j]; var eraseDist = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2)); // If element is within eraser size, remove it if (eraseDist < eraserSizeSlider.value / 2) { // Save the removed element to history for potential undo designHistory.push({ element: element, index: j, erased: true // Mark as erased rather than added }); easterEgg.designContainer.removeChild(element); break; // Remove one element at a time for better control } } } } else { var stroke = new BrushStroke(strokeColor, currentSize); easterEgg.addDesign(x, y, stroke); } } lastX = x; lastY = y; } }; game.up = function () { isPainting = false; }; // Update function game.update = function () { // Update score display scoreText.setText("Eggs: " + LK.getScore()); }; // Create and add the full color palette var colorPalette = new ColorPalette(); colorPalette.x = 2048 / 2; colorPalette.y = 2732 / 2 - 150; // Position at center, aligned with the egg, moved down by 50 units game.addChild(colorPalette); // Set up color selection callback colorPalette.onColorSelected = function (color) { currentColor = color; // Stop any animations on previously selected button if (selectedColorButton) { tween.stop(selectedColorButton); tween(selectedColorButton, { scaleX: 2, scaleY: 2 }, { duration: 100 }); } // Find button with matching color and set as selected var _loop = function _loop() { if (colorButtons[i].color === color) { var _pulseSelectedButton2 = function pulseSelectedButton() { tween(selectedColorButton, { scaleX: 2.2, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(selectedColorButton, { scaleX: 1.8, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: _pulseSelectedButton2 }); } }); }; // Start the pulsing animation selectedColorButton = colorButtons[i]; // Start pulsing animation _pulseSelectedButton2(); return 1; // break } }; for (var i = 0; i < colorButtons.length; i++) { if (_loop()) { break; } } }; // Create size slider for star stickers var sizeSlider = new Slider(0.5, 3, 1.2, 1801, 0xFFD700); // Gold color, width matches colorPanel width minus 80 units (1801) sizeSlider.x = 2048 / 2 - 900; // Position at center of map, moved left by 900 units sizeSlider.y = 2732 / 2 + 800; // Moved down by 800 units sizeSlider.visible = false; // Hidden by default game.addChild(sizeSlider); // Add label above slider var sizeLabel = new Text2("Sticker Size", { size: 80, fill: 0x000000 // Black color }); sizeLabel.anchor.set(0.5, 0.5); sizeLabel.x = sizeSlider.x + 150; sizeLabel.y = sizeSlider.y - 95; // Moved up by 25 units sizeLabel.visible = false; game.addChild(sizeLabel); // Create eraser size slider var eraserSizeSlider = new Slider(20, 100, 50, 1801, 0x444444); // Dark grey color to match clear button, width matches colorPanel width minus 80 units (1801) eraserSizeSlider.x = 2048 / 2 - 900; // Position at center of map, moved left by 900 units eraserSizeSlider.y = 2732 / 2 + 800; // Moved down to same position as star and heart slider eraserSizeSlider.visible = false; // Hidden by default game.addChild(eraserSizeSlider); // Add label for eraser size slider var eraserSizeLabel = new Text2("Eraser Size", { size: 80, fill: 0x000000 // Black color }); eraserSizeLabel.anchor.set(0.5, 0.5); eraserSizeLabel.x = eraserSizeSlider.x + 150; eraserSizeLabel.y = eraserSizeSlider.y - 95; // Moved up by 25 units eraserSizeLabel.visible = false; game.addChild(eraserSizeLabel); // Update brush size/sticker size when slider value changes sizeSlider.onValueChanged = function (value) { // Update brush size if brush tool is selected if (currentTool === 'brush') { currentBrushSize = value * 20; // Scale value to appropriate brush size } // Update sticker size for star/heart tools else if (currentTool === 'star' || currentTool === 'heart') { currentStickerSize = value; } }; // Update eraser size when eraser slider value changes eraserSizeSlider.onValueChanged = function (value) { // Update eraser size for eraser tool currentEraserSize = value; // Update any active eraser visual if it exists if (currentTool === 'eraser' && game.eraser) { var eraserVisual = game.eraser.children[0]; eraserVisual.scaleX = value / 30; eraserVisual.scaleY = value / 30; } }; // Make label visible when slider is visible game.update = function () { // Update score display scoreText.setText("Eggs: " + LK.getScore()); // Update size label visibility to match slider sizeLabel.visible = sizeSlider.visible; // Update eraser size label visibility to match slider eraserSizeLabel.visible = eraserSizeSlider.visible; }; // Start background music LK.playMusic('easterMusic', { fade: { start: 0, end: 0.2, duration: 1000 } }); // Initialize first color button as selected with animation if (colorButtons.length > 0) { var _pulseSelectedButton = function pulseSelectedButton() { tween(selectedColorButton, { scaleX: 2.2, scaleY: 2.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(selectedColorButton, { scaleX: 1.8, scaleY: 1.8 }, { duration: 500, easing: tween.easeInOut, onFinish: _pulseSelectedButton }); } }); }; // Start the initial pulsing animation selectedColorButton = colorButtons[0]; // Start pulsing animation for first color button _pulseSelectedButton(); } // Add watermark to the game with forced persistence var watermark = new Watermark("@jzubora", 0.35); game.addChild(watermark); // This interval ensures the watermark always exists, regenerating it if removed LK.setInterval(function () { // Check if watermark still exists in the game var watermarkExists = false; for (var i = 0; i < game.children.length; i++) { if (game.children[i] === watermark) { watermarkExists = true; break; } } // If watermark doesn't exist, recreate it if (!watermarkExists) { watermark = new Watermark("@jzubora", 0.35); game.addChild(watermark); // Restart the flashing animation flashWatermark(); } }, 1000); // Check every second // Add flashing animation to watermark function flashWatermark() { // Flash watermark by alternating opacity tween(watermark, { alpha: 0.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(watermark, { alpha: 0.8 }, { duration: 800, easing: tween.easeInOut, onFinish: flashWatermark }); } }); } // Start watermark flashing animation flashWatermark();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BrushStroke = Container.expand(function (color, size) {
var self = Container.call(this);
var stroke = self.attachAsset('brushStroke', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 30,
scaleY: size / 30
});
stroke.tint = color;
return self;
});
var Button = Container.expand(function (label, color) {
var self = Container.call(this);
var background = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5
});
var text = new Text2(label, {
size: 30,
fill: 0xFFFFFF
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
if (color !== undefined) {
background.tint = color;
}
self.down = function () {
tween(background, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
self.up = function () {
tween(background, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
return self;
});
var ColorButton = Container.expand(function (color) {
var self = Container.call(this);
var button = self.attachAsset('colorButton', {
anchorX: 0.5,
anchorY: 0.5
});
button.tint = color;
self.color = color;
self.down = function () {
tween(button, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
self.up = function () {
tween(button, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
};
return self;
});
var ColorPalette = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('colorPanel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1881 / 1880,
// Set to exact size 1881
scaleY: 1899 / 277 // Set to exact size 1899
});
self.addChild(background);
var colorButtons = [];
// Generate 256 color gradient palette
var paletteColors = [];
var buttonSize = 90; // Increased from 30 to 90 (3x larger)
var spacing = 12; // Increased from 4 to 12 (3x larger)
var columns = 16;
var rows = 16;
// Generate rainbow gradient colors for the full 256 color palette
for (var i = 0; i < 256; i++) {
var hue = i / 256;
var r, g, b;
// Convert HSV to RGB (simplified rainbow gradient)
if (hue < 1 / 6) {
r = 1;
g = hue * 6;
b = 0;
} else if (hue < 2 / 6) {
r = (2 / 6 - hue) * 6;
g = 1;
b = 0;
} else if (hue < 3 / 6) {
r = 0;
g = 1;
b = (hue - 2 / 6) * 6;
} else if (hue < 4 / 6) {
r = 0;
g = (4 / 6 - hue) * 6;
b = 1;
} else if (hue < 5 / 6) {
r = (hue - 4 / 6) * 6;
b = 1;
g = 0;
} else {
r = 1;
g = 0;
b = (1 - hue) * 6;
}
// Convert to 0-255 range
var red = Math.floor(r * 255);
var green = Math.floor(g * 255);
var blue = Math.floor(b * 255);
var color = red << 16 | green << 8 | blue;
paletteColors.push(color);
}
for (var i = 0; i < paletteColors.length; i++) {
var row = Math.floor(i / columns);
var col = i % columns;
var colorBtn = new ColorButton(paletteColors[i]);
colorBtn.x = col * (buttonSize + spacing) - (columns - 1) * (buttonSize + spacing) / 2;
colorBtn.y = row * (buttonSize + spacing) - (rows - 1) * (buttonSize + spacing) / 2;
colorBtn.scaleX = 4.5; // Increased from 1.5 to 4.5 (3x larger)
colorBtn.scaleY = 4.5; // Increased from 1.5 to 4.5 (3x larger)
self.addChild(colorBtn);
// Create a closure to capture the current color
(function (color, btn) {
btn.down = function () {
currentColor = color;
self.visible = false;
// Notify the game that a color was selected
if (self.onColorSelected) {
self.onColorSelected(color);
}
};
})(paletteColors[i], colorBtn);
colorButtons.push(colorBtn);
}
// Add selector ring
var selector = LK.getAsset('brushStroke', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 7.5,
// Increased from 2.5 to 7.5 (3x larger)
scaleY: 7.5 // Increased from 2.5 to 7.5 (3x larger)
});
selector.tint = 0xFFFFFF;
selector.alpha = 0.7;
self.addChild(selector);
// Set selector initial position
selector.x = colorButtons[0].x;
selector.y = colorButtons[0].y;
// Hide palette initially
self.visible = false;
// Method to update selector position
self.updateSelector = function (color) {
for (var i = 0; i < paletteColors.length; i++) {
if (paletteColors[i] === color) {
selector.x = colorButtons[i].x;
selector.y = colorButtons[i].y;
break;
}
}
};
return self;
});
var CompletedEgg = Container.expand(function (baseColor) {
var self = Container.call(this);
var eggGraphic = self.attachAsset('completedEgg', {
anchorX: 0.5,
anchorY: 0.5
});
eggGraphic.tint = baseColor || 0xFFFFFF;
// Add some random decoration to make each egg look unique
for (var i = 0; i < 5; i++) {
var glitter = self.attachAsset('glitter', {
anchorX: 0.5,
anchorY: 0.5,
x: (Math.random() - 0.5) * 80,
y: (Math.random() - 0.5) * 120
});
glitter.tint = 0xFFD700 + Math.floor(Math.random() * 0x555555);
}
return self;
});
var EasterEgg = Container.expand(function () {
var self = Container.call(this);
var eggBase = self.attachAsset('eggBase', {
anchorX: 0.5,
anchorY: 0.5
});
var designContainer = new Container();
self.designContainer = designContainer; // Make accessible outside
self.addChild(designContainer);
self.addDesign = function (x, y, designElement) {
var localPosition = designContainer.toLocal({
x: x,
y: y
}, game);
// Check if the point is within the bounds of the eggBase
// Convert to egg's local coordinates for hit testing
var eggLocalPosition = self.toLocal({
x: x,
y: y
}, game);
// Calculate distance from center of egg
var dx = eggLocalPosition.x;
var dy = eggLocalPosition.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if point is inside the egg (ellipse)
// Using the egg's dimensions to determine the ellipse bounds
var eggWidth = eggBase.width / 2;
var eggHeight = eggBase.height / 2;
// Formula for point in ellipse: (x/a)² + (y/b)² <= 1
// where a is half-width and b is half-height
var isInEgg = dx * dx / (eggWidth * eggWidth) + dy * dy / (eggHeight * eggHeight) <= 1;
// Only add design if point is inside the egg
if (isInEgg) {
designElement.x = localPosition.x;
designElement.y = localPosition.y;
designContainer.addChild(designElement);
// Add to design history for undo functionality
designHistory.push({
element: designElement,
index: designContainer.children.length - 1
});
}
};
self.clearDesigns = function () {
while (designContainer.children.length > 0) {
designContainer.removeChild(designContainer.children[0]);
}
};
self.rotate = function (angle) {
eggBase.rotation += angle;
designContainer.rotation += angle;
};
self.captureDesign = function () {
// In a real implementation, this would create a snapshot of the egg
// For this simplified version, we just create a new completed egg with the same tint
var completedEgg = new CompletedEgg(eggBase.tint);
return completedEgg;
};
return self;
});
var Eraser = Container.expand(function (size) {
var self = Container.call(this);
// Create a visual representation of the eraser
var eraserVisual = self.attachAsset('brushStroke', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 30,
scaleY: size / 30
});
// Make it white with a border
eraserVisual.tint = 0xFFFFFF;
eraserVisual.alpha = 0.7;
return self;
});
var Slider = Container.expand(function (minVal, maxVal, defaultVal, width, color) {
var self = Container.call(this);
// Track properties
self.minValue = minVal || 0;
self.maxValue = maxVal || 100;
self.value = defaultVal || 50;
self.trackWidth = width || 300;
self.trackColor = color || 0xCCCCCC;
// Create track
var track = LK.getAsset('buttonBg', {
anchorX: 0,
anchorY: 0.5,
scaleX: self.trackWidth / 120,
scaleY: 0.3
});
track.tint = self.trackColor;
self.addChild(track);
self.track = track; // Store track reference for external access
// Create handle
var handle = LK.getAsset('colorButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
handle.tint = 0xFFFFFF;
handle.x = (self.value - self.minValue) / (self.maxValue - self.minValue) * self.trackWidth;
self.addChild(handle);
self.handle = handle;
// No value label is needed
// Method definitions
// Convert value to x position
self.getXForValue = function (val) {
return (val - self.minValue) / (self.maxValue - self.minValue) * self.trackWidth;
};
// Convert x position to value
self.getValueForX = function (x) {
return self.minValue + x / self.trackWidth * (self.maxValue - self.minValue);
};
// Update handle position
self.updateHandlePosition = function () {
handle.x = self.getXForValue(self.value);
};
// Set value
self.setValue = function (val) {
self.value = Math.max(self.minValue, Math.min(self.maxValue, val));
self.updateHandlePosition();
if (self.onValueChanged) {
self.onValueChanged(self.value);
}
};
// Handle drag
self.down = function (x, y) {
self.isDragging = true;
self.setValue(self.getValueForX(x));
};
self.move = function (x, y) {
if (self.isDragging) {
self.setValue(self.getValueForX(Math.max(0, Math.min(self.trackWidth, x))));
}
};
self.up = function () {
self.isDragging = false;
};
return self;
});
var Sticker = Container.expand(function (type, scale) {
var self = Container.call(this);
var stickerAsset;
if (type === 'star') {
stickerAsset = self.attachAsset('stickerStar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale * 5,
scaleY: scale * 5
});
} else if (type === 'heart') {
stickerAsset = self.attachAsset('stickerHeart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale * 5,
scaleY: scale * 5
});
}
self.down = function () {
tween(stickerAsset, {
scaleX: scale * 5 * 0.9,
scaleY: scale * 5 * 0.9
}, {
duration: 100
});
};
self.up = function () {
tween(stickerAsset, {
scaleX: scale * 5,
scaleY: scale * 5
}, {
duration: 100
});
};
return self;
});
var Watermark = Container.expand(function (text, opacity) {
var self = Container.call(this);
// Set default values if not provided
self.text = text || "FRVR Easter";
self.opacity = opacity || 0.35;
// Create corner text elements
function createCornerText(x, y, rotation) {
var cornerText = new Text2(self.text, {
size: 28,
fill: 0x333333
});
cornerText.anchor.set(0.5, 0.5);
cornerText.x = x;
cornerText.y = y;
cornerText.rotation = rotation || 0;
cornerText.alpha = self.opacity;
self.addChild(cornerText);
return cornerText;
}
// Create text for each corner, avoiding top-left 100x100 area
self.topRight = createCornerText(2048 - 120, 120, Math.PI / 4);
self.bottomRight = createCornerText(2048 - 120, 2732 - 120, -Math.PI / 4);
self.bottomLeft = createCornerText(120, 2732 - 120, Math.PI / 4);
// Top left is placed with extra margin to avoid the platform menu icon
self.topLeft = createCornerText(180, 180, -Math.PI / 4);
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xAFEEEE // Light turquoise background color
});
/****
* Game Code
****/
// Define color palette - arranged in rainbow gradient
var colors = [
// Black (left)
0x000000,
// Violet group
0x9400D3,
// Violet
0x4B0082,
// Indigo
0x8A2BE2,
// Blue Violet
// Blue group
0x0000FF,
// Blue
0x000080,
// Navy Blue
0x00FFFF,
// Cyan
0x008080,
// Teal
// Green group
0x00FF00,
// Green
0x32CD32,
// Lime Green
0x808000,
// Olive
// Red group
0x800000,
// Maroon
0xA52A2A,
// Brown
0xFF0000,
// Red
0xFF7F00,
// Orange
// Yellow group
0xFFD700,
// Gold
0xFFFF00,
// Yellow
// Pink group
0xFF69B4,
// Hot Pink
// Gray
0x808080,
// White (right)
0xFFFFFF];
// Define brush sizes
var brushSizes = [10, 20, 30];
// Initialize game variables
var currentColor = colors[0];
var currentBrushSize = brushSizes[1];
var currentEraserSize = 50;
var currentStickerSize = 1.2;
var currentTool = 'brush';
var previousTool = null;
var isPainting = false;
var lastX, lastY;
var selectedColorButton = null;
// Keep track of actions for undo functionality
var designHistory = [];
// Initialize empty savedEggs array
var savedEggs = [];
// Basket already created earlier
// Create main egg
var easterEgg = new EasterEgg();
easterEgg.x = 2048 / 2;
easterEgg.y = 2732 / 2 - 200;
game.addChild(easterEgg);
// Create UI components
// Tool panel
var toolPanel = LK.getAsset('toolPanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 5,
y: 2732 - 100 // Moved up by 20 units
});
game.addChild(toolPanel);
// Color panel
var colorPanel = LK.getAsset('colorPanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 - 345 // Moved down by 5 units
});
game.addChild(colorPanel);
// Add color buttons
var colorButtons = [];
for (var i = 0; i < colors.length; i++) {
var colorButton = new ColorButton(colors[i]);
// Position in two rows (10 in first row, 10 in second row)
if (i < 10) {
// First row
colorButton.x = i * 130 + 160; // Changed spacing from 145 to 130, moved left by 10 units
colorButton.y = 2732 - 410; // First row - moved up by 10 units
} else {
// Second row
colorButton.x = (i - 10) * 130 + 160; // Changed spacing from 145 to 130, moved left by 10 units
colorButton.y = 2732 - 280; // Second row
}
game.addChild(colorButton);
colorButtons.push(colorButton);
// Scale up by 2x
tween(colorButton, {
scaleX: 2,
scaleY: 2
}, {
duration: 300
});
// Add click handler
(function (color, button) {
colorButton.down = function () {
tween(colorButton, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 100
});
currentColor = color;
};
colorButton.up = function () {
tween(colorButton, {
scaleX: 2,
scaleY: 2
}, {
duration: 100
});
// Stop any animations on previously selected button
if (selectedColorButton) {
tween.stop(selectedColorButton);
tween(selectedColorButton, {
scaleX: 2,
scaleY: 2
}, {
duration: 100
});
}
// Set new selected button
selectedColorButton = button;
// Start pulsing animation
function pulseButton() {
tween(selectedColorButton, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(selectedColorButton, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: pulseButton
});
}
});
}
// Start the pulsing animation
pulseButton();
};
})(colors[i], colorButton);
}
// Create tool buttons
// Calculate button width including scale factor
var buttonWidth = 120 * 2; // 120px width * 2 scale
var buttonSpacing = 25; // 25 units apart
var startX = 300; // Starting X position
// Create tool buttons
var brushButton = new Button("Brush", 0x6495ED);
brushButton.x = startX - 75; // Moved left by 25 more units
brushButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(brushButton);
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
brushButton.down = function () {
tween(brushButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
brushButton.up = function () {
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Hide any previous tool's sliders first
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Show size slider when brush tool is selected
sizeSlider.visible = true;
sizeLabel.visible = true;
// Update slider track color to match button
sizeSlider.trackColor = 0x6495ED; // Match brush button color
sizeSlider.track.tint = 0x6495ED;
// Store previous tool and set current tool to brush
previousTool = currentTool;
currentTool = 'brush';
// Stop any animations on buttons
tween.stop(brushButton);
tween.stop(stickerStarButton);
tween.stop(stickerHeartButton);
tween.stop(eraserButton);
tween.stop(rotateLeftButton);
tween.stop(rotateRightButton);
tween.stop(saveButton);
tween.stop(clearButton);
// Reset all buttons to normal size
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(saveButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Start pulsing animation for brush button
function pulseBrushButton() {
tween(brushButton, {
scaleX: 2.4,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushButton, {
scaleX: 2.0,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: pulseBrushButton
});
}
});
}
// Start the pulsing animation
pulseBrushButton();
};
var stickerStarButton = new Button("Star", 0xFFD700);
stickerStarButton.x = startX + (buttonWidth + buttonSpacing) * 1 - 75; // Moved left by 25 more units
stickerStarButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(stickerStarButton);
// Set background to gold color
stickerStarButton.children[0].tint = 0xFFD700; // Gold color
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
stickerStarButton.down = function () {
tween(stickerStarButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
stickerStarButton.up = function () {
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Store previous tool and set current tool to star
previousTool = currentTool;
currentTool = 'star';
// First hide any previously visible sliders
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Show size slider for star (not toggle)
sizeSlider.visible = true;
// Update slider track color to match button
sizeSlider.trackColor = 0xFFD700; // Gold color to match button
sizeSlider.track.tint = 0xFFD700;
// Stop any animations on buttons
tween.stop(brushButton);
tween.stop(stickerStarButton);
tween.stop(stickerHeartButton);
tween.stop(eraserButton);
tween.stop(rotateLeftButton);
tween.stop(rotateRightButton);
tween.stop(saveButton);
tween.stop(clearButton);
// Reset all buttons to normal size
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(saveButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Start pulsing animation for star button
function pulseStarButton() {
tween(stickerStarButton, {
scaleX: 2.4,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(stickerStarButton, {
scaleX: 2.0,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: pulseStarButton
});
}
});
}
// Start the pulsing animation
pulseStarButton();
};
var stickerHeartButton = new Button("Heart", 0xFF0000);
stickerHeartButton.x = startX + (buttonWidth + buttonSpacing) * 2 - 75; // Moved left by 25 more units
stickerHeartButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(stickerHeartButton);
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
stickerHeartButton.down = function () {
tween(stickerHeartButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
stickerHeartButton.up = function () {
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Store previous tool and set current tool to heart
previousTool = currentTool;
currentTool = 'heart';
// First hide any previously visible sliders
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Show size slider for heart (not toggle)
sizeSlider.visible = true;
// Update slider track color to match button
sizeSlider.trackColor = 0xFF0000; // Red color to match button
sizeSlider.track.tint = 0xFF0000;
// Keep handle white for better visibility
// Stop any animations on buttons
tween.stop(brushButton);
tween.stop(stickerStarButton);
tween.stop(stickerHeartButton);
tween.stop(eraserButton);
tween.stop(rotateLeftButton);
tween.stop(rotateRightButton);
tween.stop(saveButton);
tween.stop(clearButton);
// Reset all buttons to normal size
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(saveButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Start pulsing animation for heart button
function pulseHeartButton() {
tween(stickerHeartButton, {
scaleX: 2.4,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(stickerHeartButton, {
scaleX: 2.0,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: pulseHeartButton
});
}
});
}
// Start the pulsing animation
pulseHeartButton();
};
var rotateLeftButton = new Button("< Rotate", 0x9370DB);
rotateLeftButton.x = startX + (buttonWidth + buttonSpacing) * 3 - 75; // Moved left by 25 more units
rotateLeftButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(rotateLeftButton);
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
rotateLeftButton.down = function () {
tween(rotateLeftButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
rotateLeftButton.up = function () {
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Hide any open sliders
sizeSlider.visible = false;
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Store previous tool before rotation
previousTool = currentTool;
easterEgg.rotate(-Math.PI / 8);
};
// Add a thin grey vertical divider between rotate buttons
var divider = LK.getAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + (buttonWidth + buttonSpacing) * 3.5 - 75,
// Position between the two rotate buttons
y: 2732 - 100,
// Same Y as buttons
scaleX: 0.05,
// Make it thin
scaleY: 2 // Same height as buttons
});
divider.tint = 0xe6e6fa; // Light lavender color
game.addChild(divider);
var rotateRightButton = new Button("Rotate >", 0x9370DB);
rotateRightButton.x = startX + (buttonWidth + buttonSpacing) * 4 - 75; // Moved left by 25 more units
rotateRightButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(rotateRightButton);
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
rotateRightButton.down = function () {
tween(rotateRightButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
rotateRightButton.up = function () {
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Hide any open sliders
sizeSlider.visible = false;
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Store previous tool before rotation
previousTool = currentTool;
easterEgg.rotate(Math.PI / 8);
};
var saveButton = new Button("Undo", 0x8B4513);
saveButton.x = startX + (buttonWidth + buttonSpacing) * 5 - 75; // Moved left by 25 more units
saveButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(saveButton);
tween(saveButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
saveButton.down = function () {
tween(saveButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
// Start continuous undo functionality
var performUndo = function performUndo() {
if (designHistory.length > 0) {
var lastDesign = designHistory.pop();
// Handle clear operation undo (restore all elements that were cleared)
if (lastDesign.clearOperation) {
// First clear any current elements to avoid duplicates
easterEgg.clearDesigns();
// Add back all elements that were cleared
for (var i = 0; i < lastDesign.elements.length; i++) {
easterEgg.designContainer.addChild(lastDesign.elements[i]);
}
LK.getSound('saveSound').play();
}
// Handle erased element undo (add back erased element)
else if (lastDesign.erased) {
easterEgg.designContainer.addChildAt(lastDesign.element, lastDesign.index);
LK.getSound('saveSound').play();
}
// Handle regular element undo (remove added element)
else if (easterEgg.designContainer && easterEgg.designContainer.children.length > 0) {
// Check if the element is still at the expected index
if (easterEgg.designContainer.children[lastDesign.index] === lastDesign.element) {
easterEgg.designContainer.removeChild(lastDesign.element);
} else {
// If not at expected index, find and remove it by reference
for (var i = 0; i < easterEgg.designContainer.children.length; i++) {
if (easterEgg.designContainer.children[i] === lastDesign.element) {
easterEgg.designContainer.removeChild(lastDesign.element);
break;
}
}
}
LK.getSound('saveSound').play();
}
}
};
// Perform first undo immediately
performUndo();
// Setup interval for continuous undo while button is pressed
saveButton.undoInterval = LK.setInterval(function () {
performUndo();
}, 150); // Undo every 150ms while pressed (2x faster)
};
saveButton.up = function () {
tween(saveButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Hide any previous tool's sliders first
sizeSlider.visible = false;
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Stop continuous undo when releasing the button
if (saveButton.undoInterval) {
LK.clearInterval(saveButton.undoInterval);
saveButton.undoInterval = null;
}
// Deactivate previous tool and activate save tool
if (currentTool !== 'save') {
var _pulseSaveButton = function pulseSaveButton() {
tween(saveButton, {
scaleX: 2.4,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(saveButton, {
scaleX: 2.0,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _pulseSaveButton
});
}
});
}; // Start the pulsing animation
// Stop any animations on buttons
tween.stop(brushButton);
tween.stop(stickerStarButton);
tween.stop(stickerHeartButton);
tween.stop(eraserButton);
tween.stop(rotateLeftButton);
tween.stop(rotateRightButton);
tween.stop(saveButton);
tween.stop(clearButton);
// Reset all buttons to normal size
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Store previous tool and set current tool to save
previousTool = currentTool;
currentTool = 'save';
// Start pulsing animation for save button
_pulseSaveButton();
}
};
var eraserButton = new Button("Eraser", 0x808080);
eraserButton.x = colorPanel.x + colorPanel.width / 2 - 145; // Moved right by 100 units
eraserButton.y = colorPanel.y - colorPanel.height / 2 + 139; // Moved down to match all colors button
game.addChild(eraserButton);
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 300
});
eraserButton.down = function () {
tween(eraserButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
eraserButton.up = function () {
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
// Toggle eraser mode
if (currentTool === 'eraser') {
var _pulseBrushButton = function pulseBrushButton() {
tween(brushButton, {
scaleX: 2.4,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(brushButton, {
scaleX: 2.0,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _pulseBrushButton
});
}
});
};
// Store previous tool and switch back to brush
previousTool = currentTool;
currentTool = 'brush';
// Stop animation on eraser button
tween.stop(eraserButton);
tween(eraserButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
// Hide eraser size slider
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Start pulsing animation for brush button
_pulseBrushButton();
} else {
var _pulseEraserButton = function pulseEraserButton() {
tween(eraserButton, {
scaleX: 2.4,
scaleY: 3.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(eraserButton, {
scaleX: 2.0,
scaleY: 2.9
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _pulseEraserButton
});
}
});
};
// Store previous tool and switch to eraser mode
previousTool = currentTool;
currentTool = 'eraser';
// Stop animations on all other buttons
tween.stop(brushButton);
tween.stop(stickerStarButton);
tween.stop(stickerHeartButton);
tween.stop(rotateLeftButton);
tween.stop(rotateRightButton);
tween.stop(clearButton);
// Reset all buttons to normal size
tween(brushButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerStarButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(stickerHeartButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateLeftButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(rotateRightButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// First hide any previously visible sliders
sizeSlider.visible = false;
// Show eraser size slider
eraserSizeSlider.visible = true;
eraserSizeLabel.visible = true;
// Start pulsing animation for eraser button
_pulseEraserButton();
}
};
// Add color palette button
var allColorButton = new Button("All Colors", 0x9370DB);
allColorButton.x = colorPanel.x + colorPanel.width / 2 - 415; // Position it - moved left by 100 units (from -315 to -415)
allColorButton.y = colorPanel.y - colorPanel.height / 2 + 139; // Moved down by 4 units (from 135 to 139)
game.addChild(allColorButton);
tween(allColorButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 300
});
// Apply rainbow gradient to all colors button
var btnBackground = allColorButton.children[0];
var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3];
var colorIndex = 0;
var duration = 3000; // 3 seconds for full rainbow cycle
// Function to cycle through rainbow colors
function cycleRainbowColors() {
tween(btnBackground, {
tint: rainbowColors[colorIndex]
}, {
duration: duration / rainbowColors.length,
onFinish: function onFinish() {
colorIndex = (colorIndex + 1) % rainbowColors.length;
cycleRainbowColors();
}
});
}
// Start the rainbow cycle
cycleRainbowColors();
allColorButton.down = function () {
tween(allColorButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
allColorButton.up = function () {
// Reset to normal size first
tween(allColorButton, {
scaleX: 2.2,
scaleY: 3.1
}, {
duration: 100
});
// Toggle the full color palette
colorPalette.visible = !colorPalette.visible;
// Update selector position to match current selected color
if (colorPalette.visible) {
colorPalette.updateSelector(currentColor);
}
};
var clearButton = new Button("Clear", 0x444444);
clearButton.x = startX + (buttonWidth + buttonSpacing) * 6 - 75; // Moved next to save button
clearButton.y = 2732 - 100; // Moved up by 20 units
game.addChild(clearButton);
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 300
});
clearButton.down = function () {
tween(clearButton, {
scaleX: 1.98,
scaleY: 1.8
}, {
duration: 100
});
};
clearButton.up = function () {
tween(clearButton, {
scaleX: 2.2,
scaleY: 2
}, {
duration: 100
});
// Hide any open sliders
sizeSlider.visible = false;
eraserSizeSlider.visible = false;
eraserSizeLabel.visible = false;
// Store previous tool before clearing
previousTool = currentTool;
// Save the current design state for potential undo
if (easterEgg.designContainer && easterEgg.designContainer.children.length > 0) {
// Create a special history entry for the clear operation
designHistory.push({
clearOperation: true,
elements: easterEgg.designContainer.children.slice() // Make a copy of all elements
});
}
easterEgg.clearDesigns();
LK.getSound('clearSound').play();
};
// Function to toggle eraser tool
function toggleEraserThicknessSlider(visible) {
// Set eraser as current tool when enabled
if (visible) {
currentTool = 'eraser';
}
}
// Create basket display
var basket = LK.getAsset('basket', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 - 525 // Moved up by 25 units (from 500 to 525)
});
game.addChildAt(basket, 0); // Add at index 0 to ensure it's behind the egg
// Bring the easterEgg to the front to ensure proper display order
game.removeChild(easterEgg);
game.addChild(easterEgg);
// Title text
var titleText = new Text2("Easter Egg Artist", {
size: 100,
fill: 0x8B4513 // Dark brown
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 50;
game.addChild(titleText);
// Score display - hidden but kept in code for reference
var scoreText = new Text2("Eggs: 0", {
size: 60,
fill: 0x4B0082 // Indigo
});
scoreText.anchor.set(0, 0);
scoreText.x = 120;
scoreText.y = 120;
scoreText.visible = false; // Hide the score text
// Function to display saved eggs (placeholder for new implementation)
function displaySavedEggs() {
// Remove any existing displayed eggs
for (var i = game.children.length - 1; i >= 0; i--) {
if (game.children[i].isCompletedEgg) {
game.removeChild(game.children[i]);
}
}
// Display saved eggs (will be reimplemented)
for (var i = 0; i < savedEggs.length; i++) {
var eggData = savedEggs[i];
var displayedEgg = new CompletedEgg(eggData.color);
displayedEgg.x = eggData.x;
displayedEgg.y = eggData.y;
displayedEgg.isCompletedEgg = true;
game.addChild(displayedEgg);
}
}
// Display any saved eggs
displaySavedEggs();
// Game event handlers
game.down = function (x, y) {
if (y < 2732 - 350) {
isPainting = true;
lastX = x;
lastY = y;
if (currentTool === 'brush') {
var brushStroke = new BrushStroke(currentColor, currentBrushSize);
easterEgg.addDesign(x, y, brushStroke);
LK.getSound('brushSound').play();
} else if (currentTool === 'star' || currentTool === 'heart') {
var size = currentStickerSize;
var sticker = new Sticker(currentTool, size);
easterEgg.addDesign(x, y, sticker);
LK.getSound('stickerSound').play();
} else if (currentTool === 'eraser') {
// Create a visual eraser element but don't actually add it to the design
var eraser = new Eraser(eraserSizeSlider.value);
// Store reference to enable updating size from slider
game.eraser = eraser;
// Convert to egg's local coordinates for hit testing
var eggLocalPosition = easterEgg.toLocal({
x: x,
y: y
}, game);
// Check if point is inside egg boundary
var eggBase = easterEgg.children[0]; // The eggBase is the first child
var eggWidth = eggBase.width / 2;
var eggHeight = eggBase.height / 2;
var dx = eggLocalPosition.x;
var dy = eggLocalPosition.y;
var isInEgg = dx * dx / (eggWidth * eggWidth) + dy * dy / (eggHeight * eggHeight) <= 1;
if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) {
// Find and remove elements near the eraser position
for (var i = easterEgg.designContainer.children.length - 1; i >= 0; i--) {
var element = easterEgg.designContainer.children[i];
var distance = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2));
// If element is within eraser size, remove it
if (distance < eraserSizeSlider.value / 2) {
// Save the removed element to history for potential undo
designHistory.push({
element: element,
index: i,
erased: true // Mark as erased rather than added
});
easterEgg.designContainer.removeChild(element);
LK.getSound('brushSound').play();
break; // Remove one element at a time for better control
}
}
}
}
}
};
game.move = function (x, y) {
if (isPainting && (currentTool === 'brush' || currentTool === 'eraser')) {
// Calculate distance between last point and current point
var dx = x - lastX;
var dy = y - lastY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Get current size based on tool
var currentSize = currentTool === 'brush' ? currentBrushSize : currentEraserSize;
// Get current color based on tool
var strokeColor = currentTool === 'brush' ? currentColor : 0xFFFFFF;
// Play brush sound occasionally while using eraser
if (currentTool === 'eraser' && Math.random() < 0.05) {
LK.getSound('brushSound').play();
}
// If distance is greater than half the size, add intermediate points
if (distance > currentSize / 2) {
var steps = Math.ceil(distance / (currentSize / 2));
for (var i = 1; i <= steps; i++) {
var pointX = lastX + dx * i / steps;
var pointY = lastY + dy * i / steps;
if (currentTool === 'eraser') {
// Calculate egg local position for eraser
var eggLocalPosition = easterEgg.toLocal({
x: pointX,
y: pointY
}, game);
// Check if point is inside egg boundary
var eggBase = easterEgg.children[0];
var eggWidth = eggBase.width / 2;
var eggHeight = eggBase.height / 2;
var localDx = eggLocalPosition.x;
var localDy = eggLocalPosition.y;
var isInEgg = localDx * localDx / (eggWidth * eggWidth) + localDy * localDy / (eggHeight * eggHeight) <= 1;
if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) {
// Find and remove elements near the eraser position
for (var j = easterEgg.designContainer.children.length - 1; j >= 0; j--) {
var element = easterEgg.designContainer.children[j];
var eraseDist = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2));
// If element is within eraser size, remove it
if (eraseDist < eraserSizeSlider.value / 2) {
// Save the removed element to history for potential undo
designHistory.push({
element: element,
index: j,
erased: true // Mark as erased rather than added
});
easterEgg.designContainer.removeChild(element);
break; // Remove one element at a time for better control
}
}
}
} else {
var stroke = new BrushStroke(strokeColor, currentSize);
easterEgg.addDesign(pointX, pointY, stroke);
}
}
} else {
if (currentTool === 'eraser') {
// Calculate egg local position for eraser
var eggLocalPosition = easterEgg.toLocal({
x: x,
y: y
}, game);
// Check if point is inside egg boundary
var eggBase = easterEgg.children[0];
var eggWidth = eggBase.width / 2;
var eggHeight = eggBase.height / 2;
var localDx = eggLocalPosition.x;
var localDy = eggLocalPosition.y;
var isInEgg = localDx * localDx / (eggWidth * eggWidth) + localDy * localDy / (eggHeight * eggHeight) <= 1;
if (isInEgg && easterEgg.designContainer && easterEgg.designContainer.children.length > 0) {
// Find and remove elements near the eraser position
for (var j = easterEgg.designContainer.children.length - 1; j >= 0; j--) {
var element = easterEgg.designContainer.children[j];
var eraseDist = Math.sqrt(Math.pow(element.x - eggLocalPosition.x, 2) + Math.pow(element.y - eggLocalPosition.y, 2));
// If element is within eraser size, remove it
if (eraseDist < eraserSizeSlider.value / 2) {
// Save the removed element to history for potential undo
designHistory.push({
element: element,
index: j,
erased: true // Mark as erased rather than added
});
easterEgg.designContainer.removeChild(element);
break; // Remove one element at a time for better control
}
}
}
} else {
var stroke = new BrushStroke(strokeColor, currentSize);
easterEgg.addDesign(x, y, stroke);
}
}
lastX = x;
lastY = y;
}
};
game.up = function () {
isPainting = false;
};
// Update function
game.update = function () {
// Update score display
scoreText.setText("Eggs: " + LK.getScore());
};
// Create and add the full color palette
var colorPalette = new ColorPalette();
colorPalette.x = 2048 / 2;
colorPalette.y = 2732 / 2 - 150; // Position at center, aligned with the egg, moved down by 50 units
game.addChild(colorPalette);
// Set up color selection callback
colorPalette.onColorSelected = function (color) {
currentColor = color;
// Stop any animations on previously selected button
if (selectedColorButton) {
tween.stop(selectedColorButton);
tween(selectedColorButton, {
scaleX: 2,
scaleY: 2
}, {
duration: 100
});
}
// Find button with matching color and set as selected
var _loop = function _loop() {
if (colorButtons[i].color === color) {
var _pulseSelectedButton2 = function pulseSelectedButton() {
tween(selectedColorButton, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(selectedColorButton, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _pulseSelectedButton2
});
}
});
}; // Start the pulsing animation
selectedColorButton = colorButtons[i];
// Start pulsing animation
_pulseSelectedButton2();
return 1; // break
}
};
for (var i = 0; i < colorButtons.length; i++) {
if (_loop()) {
break;
}
}
};
// Create size slider for star stickers
var sizeSlider = new Slider(0.5, 3, 1.2, 1801, 0xFFD700); // Gold color, width matches colorPanel width minus 80 units (1801)
sizeSlider.x = 2048 / 2 - 900; // Position at center of map, moved left by 900 units
sizeSlider.y = 2732 / 2 + 800; // Moved down by 800 units
sizeSlider.visible = false; // Hidden by default
game.addChild(sizeSlider);
// Add label above slider
var sizeLabel = new Text2("Sticker Size", {
size: 80,
fill: 0x000000 // Black color
});
sizeLabel.anchor.set(0.5, 0.5);
sizeLabel.x = sizeSlider.x + 150;
sizeLabel.y = sizeSlider.y - 95; // Moved up by 25 units
sizeLabel.visible = false;
game.addChild(sizeLabel);
// Create eraser size slider
var eraserSizeSlider = new Slider(20, 100, 50, 1801, 0x444444); // Dark grey color to match clear button, width matches colorPanel width minus 80 units (1801)
eraserSizeSlider.x = 2048 / 2 - 900; // Position at center of map, moved left by 900 units
eraserSizeSlider.y = 2732 / 2 + 800; // Moved down to same position as star and heart slider
eraserSizeSlider.visible = false; // Hidden by default
game.addChild(eraserSizeSlider);
// Add label for eraser size slider
var eraserSizeLabel = new Text2("Eraser Size", {
size: 80,
fill: 0x000000 // Black color
});
eraserSizeLabel.anchor.set(0.5, 0.5);
eraserSizeLabel.x = eraserSizeSlider.x + 150;
eraserSizeLabel.y = eraserSizeSlider.y - 95; // Moved up by 25 units
eraserSizeLabel.visible = false;
game.addChild(eraserSizeLabel);
// Update brush size/sticker size when slider value changes
sizeSlider.onValueChanged = function (value) {
// Update brush size if brush tool is selected
if (currentTool === 'brush') {
currentBrushSize = value * 20; // Scale value to appropriate brush size
}
// Update sticker size for star/heart tools
else if (currentTool === 'star' || currentTool === 'heart') {
currentStickerSize = value;
}
};
// Update eraser size when eraser slider value changes
eraserSizeSlider.onValueChanged = function (value) {
// Update eraser size for eraser tool
currentEraserSize = value;
// Update any active eraser visual if it exists
if (currentTool === 'eraser' && game.eraser) {
var eraserVisual = game.eraser.children[0];
eraserVisual.scaleX = value / 30;
eraserVisual.scaleY = value / 30;
}
};
// Make label visible when slider is visible
game.update = function () {
// Update score display
scoreText.setText("Eggs: " + LK.getScore());
// Update size label visibility to match slider
sizeLabel.visible = sizeSlider.visible;
// Update eraser size label visibility to match slider
eraserSizeLabel.visible = eraserSizeSlider.visible;
};
// Start background music
LK.playMusic('easterMusic', {
fade: {
start: 0,
end: 0.2,
duration: 1000
}
});
// Initialize first color button as selected with animation
if (colorButtons.length > 0) {
var _pulseSelectedButton = function pulseSelectedButton() {
tween(selectedColorButton, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(selectedColorButton, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _pulseSelectedButton
});
}
});
}; // Start the initial pulsing animation
selectedColorButton = colorButtons[0];
// Start pulsing animation for first color button
_pulseSelectedButton();
}
// Add watermark to the game with forced persistence
var watermark = new Watermark("@jzubora", 0.35);
game.addChild(watermark);
// This interval ensures the watermark always exists, regenerating it if removed
LK.setInterval(function () {
// Check if watermark still exists in the game
var watermarkExists = false;
for (var i = 0; i < game.children.length; i++) {
if (game.children[i] === watermark) {
watermarkExists = true;
break;
}
}
// If watermark doesn't exist, recreate it
if (!watermarkExists) {
watermark = new Watermark("@jzubora", 0.35);
game.addChild(watermark);
// Restart the flashing animation
flashWatermark();
}
}, 1000); // Check every second
// Add flashing animation to watermark
function flashWatermark() {
// Flash watermark by alternating opacity
tween(watermark, {
alpha: 0.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(watermark, {
alpha: 0.8
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: flashWatermark
});
}
});
}
// Start watermark flashing animation
flashWatermark();