/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Dot class for interactive dots in the puzzle
var Dot = Container.expand(function () {
var self = Container.call(this);
// Create a visual representation for the dot
var dotGraphics = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
// Dot properties
self.isActive = false;
self.gridX = 0;
self.gridY = 0;
self.instrumentIndex = 0;
self.currentInstrument = null;
// Array of instrument image IDs for first row (dots 1-5)
self.instruments = ['Blank', 'Saxophone', 'Drum', 'Gutair', 'Harp', 'Flute'];
// Array of image IDs for dots 21-25 (bottom row)
self.specialInstruments = ['Blank', 'PJ', 'KJ', 'Harmony', 'Khalida', 'Sapphire'];
// Method to toggle dot state
self.toggle = function () {
self.isActive = !self.isActive;
};
// Method to cycle through instruments for first row (dots 1-5)
self.cycleInstrument = function () {
// Remove current instrument if it exists and free it from usedInstruments
if (self.currentInstrument) {
var currentInstrumentId = self.instruments[self.instrumentIndex];
if (currentInstrumentId !== 'Blank' && usedInstruments[currentInstrumentId] === self) {
delete usedInstruments[currentInstrumentId];
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Find next available instrument
var startIndex = self.instrumentIndex;
var found = false;
do {
self.instrumentIndex = (self.instrumentIndex + 1) % self.instruments.length;
var instrumentId = self.instruments[self.instrumentIndex];
// Check if instrument is available (Blank is always available)
if (instrumentId === 'Blank' || !usedInstruments[instrumentId]) {
found = true;
// Mark non-blank instruments as used
if (instrumentId !== 'Blank') {
usedInstruments[instrumentId] = self;
}
// Create new instrument image
self.currentInstrument = self.attachAsset(instrumentId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
} while (self.instrumentIndex !== startIndex);
// If no available instrument found, stay on current (shouldn't happen with 6 instruments for 5 dots)
if (!found) {
self.instrumentIndex = startIndex;
}
};
// Method to cycle through special images for dots 21-25 (bottom row)
self.cycleSpecialInstrument = function () {
// Remove current instrument if it exists
if (self.currentInstrument) {
// Remove from usedSpecialInstruments if not Blank
if (typeof usedSpecialInstruments !== "undefined" && self.specialInstruments && typeof self.specialInstrumentIndex === "number") {
var prevId = self.specialInstruments[self.specialInstrumentIndex];
if (prevId !== 'Blank' && usedSpecialInstruments[prevId] === self) {
delete usedSpecialInstruments[prevId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Find next available special instrument (no duplicates except Blank)
if (typeof usedSpecialInstruments === "undefined") {
usedSpecialInstruments = {};
}
if (typeof self.specialInstrumentIndex !== "number") self.specialInstrumentIndex = 0;
self.specialInstrumentIndex = typeof self.specialInstrumentIndex === "number" ? self.specialInstrumentIndex : 0;
var startIndex = self.specialInstrumentIndex;
var found = false;
do {
self.specialInstrumentIndex = (self.specialInstrumentIndex + 1) % self.specialInstruments.length;
var instrumentId = self.specialInstruments[self.specialInstrumentIndex];
// Only allow Blank or unused
if (instrumentId === 'Blank' || !usedSpecialInstruments[instrumentId]) {
found = true;
// Mark as used if not Blank
if (instrumentId !== 'Blank') {
usedSpecialInstruments[instrumentId] = self;
}
// Slightly decrease the size of all special images for dots 21-25
var scaleX = 0.85;
var scaleY = 0.85;
if (instrumentId === 'Harmony') {
scaleX = 1.45;
scaleY = 1.45;
}
// Move Khalida or Harmony image up by 10 pixels, move KJ down by 5 pixels
var extraY = 0;
if (instrumentId === 'Khalida' || instrumentId === 'Harmony') {
extraY = -10;
}
if (instrumentId === 'KJ') {
extraY = 5;
}
self.currentInstrument = self.attachAsset(instrumentId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scaleX,
scaleY: scaleY,
y: extraY
});
break;
}
} while (self.specialInstrumentIndex !== startIndex);
// If no available instrument found, stay on current (shouldn't happen)
if (!found) {
self.specialInstrumentIndex = startIndex;
}
};
// Touch/click handler
self.down = function (x, y, obj) {
// Only cycle instruments for dots 1-5 (first row)
if (self.gridY === 0) {
self.cycleInstrument();
}
// Dots 21-25 (bottom row, gridY === 4, gridX 0-4)
else if (self.gridY === 4) {
self.cycleSpecialInstrument();
}
// Dots 16-20 (row 4, gridY === 3, gridX 0-4)
else if (self.gridY === 3) {
// Animal images to cycle through
if (!self.animalImages) {
self.animalImages = ['Blank', 'Cat', 'Dog', 'Fish', 'Frog', 'Turtle'];
self.animalIndex = 0;
}
// Setup usedAnimalImages global tracker if not present
if (typeof usedAnimalImages === "undefined") {
usedAnimalImages = {};
}
// Remove current animal image if exists and update usedAnimalImages
if (self.currentInstrument) {
if (typeof self.animalIndex === "number") {
var prevAnimalId = self.animalImages[self.animalIndex];
if (prevAnimalId !== 'Blank' && usedAnimalImages[prevAnimalId] === self) {
delete usedAnimalImages[prevAnimalId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available animal image (no duplicates except Blank)
var startIndex = typeof self.animalIndex === "number" ? self.animalIndex : 0;
var found = false;
var tries = 0;
do {
self.animalIndex = (typeof self.animalIndex === "number" ? self.animalIndex : 0) + 1;
if (self.animalIndex >= self.animalImages.length) self.animalIndex = 0;
var animalId = self.animalImages[self.animalIndex];
// Only allow Blank or unused
if (animalId === 'Blank' || !usedAnimalImages[animalId]) {
found = true;
// Mark as used if not Blank
if (animalId !== 'Blank') {
usedAnimalImages[animalId] = self;
}
self.currentInstrument = self.attachAsset(animalId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
tries++;
} while (self.animalIndex !== startIndex && tries < self.animalImages.length + 1);
// If no available animal found, stay on current (shouldn't happen)
if (!found) {
self.animalIndex = startIndex;
}
}
// Dots 6-10 (row 2, gridY === 1, gridX 0-4)
else if (self.gridY === 1) {
// Food images to cycle through
if (!self.foodImages) {
self.foodImages = ['Blank', 'Pizza', 'Chips', 'Icecream', 'Donut', 'Spaghetti'];
self.foodIndex = 0;
}
// Setup usedFoodImages global tracker if not present
if (typeof usedFoodImages === "undefined") {
usedFoodImages = {};
}
// Remove current food image if exists and update usedFoodImages
if (self.currentInstrument) {
if (typeof self.foodIndex === "number") {
var prevFoodId = self.foodImages[self.foodIndex];
if (prevFoodId !== 'Blank' && usedFoodImages[prevFoodId] === self) {
delete usedFoodImages[prevFoodId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available food image (no duplicates except Blank)
var startFoodIndex = typeof self.foodIndex === "number" ? self.foodIndex : 0;
var foundFood = false;
var foodTries = 0;
do {
self.foodIndex = (typeof self.foodIndex === "number" ? self.foodIndex : 0) + 1;
if (self.foodIndex >= self.foodImages.length) self.foodIndex = 0;
var foodId = self.foodImages[self.foodIndex];
// Only allow Blank or unused
if (foodId === 'Blank' || !usedFoodImages[foodId]) {
foundFood = true;
// Mark as used if not Blank
if (foodId !== 'Blank') {
usedFoodImages[foodId] = self;
}
self.currentInstrument = self.attachAsset(foodId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
foodTries++;
} while (self.foodIndex !== startFoodIndex && foodTries < self.foodImages.length + 1);
// If no available food found, stay on current (shouldn't happen)
if (!foundFood) {
self.foodIndex = startFoodIndex;
}
} else if (self.gridY === 2) {
// Toy images to cycle through
if (!self.toyImages) {
self.toyImages = ['Blank', 'Lego', 'Dolls', 'Basketball', 'Videogame', 'Plushtoy'];
self.toyIndex = 0;
}
// Setup usedToyImages global tracker if not present
if (typeof usedToyImages === "undefined") {
usedToyImages = {};
}
// Remove current toy image if exists and update usedToyImages
if (self.currentInstrument) {
if (typeof self.toyIndex === "number") {
var prevToyId = self.toyImages[self.toyIndex];
if (prevToyId !== 'Blank' && usedToyImages[prevToyId] === self) {
delete usedToyImages[prevToyId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available toy image (no duplicates except Blank)
var startToyIndex = typeof self.toyIndex === "number" ? self.toyIndex : 0;
var foundToy = false;
var toyTries = 0;
do {
self.toyIndex = (typeof self.toyIndex === "number" ? self.toyIndex : 0) + 1;
if (self.toyIndex >= self.toyImages.length) self.toyIndex = 0;
var toyId = self.toyImages[self.toyIndex];
// Only allow Blank or unused
if (toyId === 'Blank' || !usedToyImages[toyId]) {
foundToy = true;
// Mark as used if not Blank
if (toyId !== 'Blank') {
usedToyImages[toyId] = self;
}
self.currentInstrument = self.attachAsset(toyId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
toyTries++;
} while (self.toyIndex !== startToyIndex && toyTries < self.toyImages.length + 1);
// If no available toy found, stay on current (shouldn't happen)
if (!foundToy) {
self.toyIndex = startToyIndex;
}
}
self.toggle();
// Update clues with strikethrough after changing dot
if (typeof updateCluesWithStrikethrough === 'function') {
updateCluesWithStrikethrough();
}
};
// Initialize dot as inactive
// Initialize with blank instrument
self.currentInstrument = self.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
return self;
});
// Puzzle class for managing a puzzle element in the game
var Puzzle = Container.expand(function () {
var self = Container.call(this);
// Add a 4x5 asset to the puzzle and scale it to be exactly 2048 wide
var targetWidth = 2048;
var assetWidth = 1000;
var scale = targetWidth / assetWidth;
// Center horizontally, align to top
var puzzleImage = self.attachAsset('4x5', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0,
scaleX: scale,
scaleY: scale
});
// (Story asset removed)
// (Clue images and clue text removed)
// Create 25 dots in a 5x5 grid
self.dots = [];
var gridSize = 5;
var dotSpacing = targetWidth / (gridSize + 1); // Distribute dots evenly across the width
var gridHeight = puzzleImage.height * scale;
var verticalSpacing = gridHeight / (gridSize + 1);
for (var row = 0; row < gridSize; row++) {
for (var col = 0; col < gridSize; col++) {
var dot = new Dot();
dot.gridX = col;
dot.gridY = row;
// Initialize all indices to 0 for consistent reset state
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Position dots over the 4x5 asset
dot.x = (col + 1) * dotSpacing;
dot.y = (row + 1) * verticalSpacing;
self.dots.push(dot);
self.addChild(dot);
}
}
// Generate randomized solution and clues
var generator = new PuzzleGenerator();
self.targetSolution = generator.generateSolution();
self.generatedClues = generator.generateClues(self.targetSolution);
// Example: puzzle state
self.isSolved = false;
// Method to check if puzzle is solved according to target solution
self.checkSolved = function () {
// Use the generated target solution
var targetSolution = self.targetSolution || {};
// Check if all dots match the target solution
var allCorrect = true;
for (var i = 0; i < self.dots.length; i++) {
var dot = self.dots[i];
var expectedImage = targetSolution[i];
// Get current image of the dot based on its row and current index
var currentImage = 'Blank';
var row = dot.gridY;
if (row === 0 && dot.instruments) {
// Row 1: Music instruments
currentImage = dot.instruments[dot.instrumentIndex];
} else if (row === 1 && dot.foodImages) {
// Row 2: Food
currentImage = dot.foodImages[dot.foodIndex];
} else if (row === 2 && dot.toyImages) {
// Row 3: Toys
currentImage = dot.toyImages[dot.toyIndex];
} else if (row === 3 && dot.animalImages) {
// Row 4: Animals
currentImage = dot.animalImages[dot.animalIndex];
} else if (row === 4 && dot.specialInstruments) {
// Row 5: Family
currentImage = dot.specialInstruments[dot.specialInstrumentIndex];
}
// Check if current image matches expected
if (currentImage !== expectedImage) {
allCorrect = false;
break;
}
}
// Update solved state only - win display handled by green tick
if (allCorrect && !self.isSolved) {
self.isSolved = true;
} else if (!allCorrect && self.isSolved) {
self.isSolved = false;
}
};
// Example: update method for per-frame logic
self.update = function () {
// Add per-frame puzzle logic here
// For example, check for completion
self.checkSolved();
};
return self;
});
// PuzzleGenerator class for creating randomized puzzle solutions and clues
var PuzzleGenerator = Container.expand(function () {
var self = Container.call(this);
// Available items for each category (row) - expanded to include all factors
self.categories = {
0: ['Drum', 'Harp', 'Saxophone', 'Flute', 'Gutair'],
// Instruments - matches self.instruments array in Dot class
1: ['Pizza', 'Chips', 'Icecream', 'Donut', 'Spaghetti'],
// Food - matches self.foodImages array in Dot class
2: ['Basketball', 'Dolls', 'Lego', 'Videogame', 'Plushtoy'],
// Toys - matches self.toyImages array in Dot class
3: ['Fish', 'Turtle', 'Frog', 'Cat', 'Dog'],
// Animals - matches self.animalImages array in Dot class
4: ['KJ', 'Khalida', 'Sapphire', 'Harmony', 'PJ'] // People - matches self.specialInstruments array in Dot class
};
// Generate a random valid solution
self.generateSolution = function () {
var solution = {};
// For each row, randomly assign the 5 items to the 5 positions
for (var row = 0; row < 5; row++) {
var items = self.categories[row].slice(); // Copy array
// Shuffle items using Fisher-Yates algorithm
for (var i = items.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = items[i];
items[i] = items[j];
items[j] = temp;
}
// Assign shuffled items to positions
for (var col = 0; col < 5; col++) {
var dotIndex = row * 5 + col;
solution[dotIndex] = items[col];
}
}
return solution;
};
// Generate clues based on the solution
self.generateClues = function (solution) {
var clues = [];
var usedClues = {}; // Track used clues to prevent duplicates
// Validate solution exists and has required data
if (!solution || _typeof2(solution) !== 'object') {
return clues;
}
// Helper function to validate clue items
function isValidItem(item) {
return item && typeof item === 'string' && item !== 'Blank';
}
// Helper function to create unique clue key
function createClueKey(clue) {
if (clue.type === 'equals') {
var items = [clue.item1, clue.item2].sort();
return clue.type + ':' + items[0] + '=' + items[1];
} else if (clue.type === 'order') {
return clue.type + ':' + clue.item1 + clue.operator + clue.item2;
} else if (clue.type === 'position') {
return clue.type + ':' + clue.item1 + '=' + clue.position;
}
return '';
}
// Strategy: Generate more targeted clues that create logical chains
// 1. Start with position clues for anchor points (all columns for better coverage)
var anchorPositions = [0, 1, 2, 3, 4]; // all columns 1-5 for comprehensive coverage
for (var i = 0; i < anchorPositions.length && clues.length < 8; i++) {
var col = anchorPositions[i];
var row = Math.floor(Math.random() * 5);
var item = solution[row * 5 + col];
if (isValidItem(item)) {
var clueCandidate = {
type: 'position',
item1: item,
position: col + 1
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
}
// 2. Generate strategic equality clues - ensure coverage across all columns
var equalityAttempts = 0;
while (clues.length < 15 && equalityAttempts < 50) {
// Distribute equality clues across all columns for better coverage
var col = Math.floor(Math.random() * 5);
var row1 = Math.floor(Math.random() * 5);
var row2;
do {
row2 = Math.floor(Math.random() * 5);
} while (row2 === row1);
var item1 = solution[row1 * 5 + col];
var item2 = solution[row2 * 5 + col];
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'equals',
item1: item1,
item2: item2
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
equalityAttempts++;
}
// 3. Generate ordering clues that create logical connections across all rows
var orderAttempts = 0;
while (clues.length < 22 && orderAttempts < 80) {
var row = Math.floor(Math.random() * 5);
var col1 = Math.floor(Math.random() * 4);
var col2 = col1 + 1;
var item1 = solution[row * 5 + col1];
var item2 = solution[row * 5 + col2];
var operator = '<';
// Check for existing clue connections to prioritize logical chains
var hasExistingClue = false;
for (var c = 0; c < clues.length; c++) {
if (clues[c].item1 === item1 || clues[c].item1 === item2 || clues[c].item2 === item1 || clues[c].item2 === item2) {
hasExistingClue = true;
break;
}
}
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'order',
item1: item1,
item2: item2,
operator: operator
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
// Accept all valid ordering clues to ensure comprehensive coverage
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
orderAttempts++;
}
// 4. Fill remaining slots with additional strategic clues
var fillAttempts = 0;
while (clues.length < 25 && fillAttempts < 30) {
var clueType = Math.random();
if (clueType < 0.4) {
// Additional position clues for remaining columns
var col = Math.floor(Math.random() * 5);
var row = Math.floor(Math.random() * 5);
var item = solution[row * 5 + col];
if (isValidItem(item)) {
var clueCandidate = {
type: 'position',
item1: item,
position: col + 1
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
} else {
// Additional equality clues
var col = Math.floor(Math.random() * 5);
var row1 = Math.floor(Math.random() * 5);
var row2;
do {
row2 = Math.floor(Math.random() * 5);
} while (row2 === row1);
var item1 = solution[row1 * 5 + col];
var item2 = solution[row2 * 5 + col];
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'equals',
item1: item1,
item2: item2
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
}
fillAttempts++;
}
return clues;
};
return self;
});
// StoryScene class for displaying story content
var StoryScene = Container.expand(function () {
var self = Container.call(this);
// Add background
var bg = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.95
});
// Add story content container
var storyContent = new Container();
self.addChild(storyContent);
// --- SCENE DATA ---
var scenes = [{
// First scene
imageId: 'First',
text: "Once upon a time, in a magical puzzle world,\nthere lived characters who needed your help\nto solve their mysteries..."
}, {
// Second scene
imageId: 'Second',
text: "Each puzzle you solve brings harmony to their world.\nAre you ready to begin your adventure?"
}];
var currentScene = 0;
// Add story title
var storyTitle = new Text2("STORY", {
size: 120,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
storyTitle.anchor.set(0.5, 0.5);
storyTitle.x = 2048 / 2;
storyTitle.y = 300;
storyContent.addChild(storyTitle);
// Add story image (centered, fills screen)
var storyImage = storyContent.attachAsset(scenes[0].imageId, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
// Add story text
var storyText = new Text2(scenes[0].text, {
size: 80,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
storyText.anchor.set(0.5, 0.5);
storyText.x = 2048 / 2;
storyText.y = 2732 - 600;
storyContent.addChild(storyText);
// Add continue button
var continueText = new Text2("TAP TO CONTINUE", {
size: 100,
fill: 0xFFFF00,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 2048 / 2;
continueText.y = 2732 - 400;
storyContent.addChild(continueText);
// Pulse the continue text
var _pulseContinueText = function pulseContinueText() {
tween(continueText, {
alpha: 0.3
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(continueText, {
alpha: 1
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: _pulseContinueText
});
}
});
};
_pulseContinueText();
// Helper to update scene visuals with smooth fade transition
function showScene(idx) {
if (idx < 0 || idx >= scenes.length) return;
// Fade out current image and text, then swap and fade in
var fadeOutDuration = 350;
var fadeInDuration = 350;
// Helper to fade in new image and text
function fadeInNewScene() {
// Swap image
if (storyImage && storyImage.parent) {
storyImage.parent.removeChild(storyImage);
}
var newImg = storyContent.attachAsset(scenes[idx].imageId, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
newImg.alpha = 0;
storyImage = newImg;
// Update text
storyText.setText(scenes[idx].text);
storyText.alpha = 0;
// Fade in new image and text
tween(newImg, {
alpha: 1
}, {
duration: fadeInDuration,
easing: tween.cubicOut
});
tween(storyText, {
alpha: 1
}, {
duration: fadeInDuration,
easing: tween.cubicOut
});
}
// Fade out current image and text, then call fadeInNewScene
if (storyImage && storyText) {
tween(storyImage, {
alpha: 0
}, {
duration: fadeOutDuration,
easing: tween.cubicIn,
onFinish: fadeInNewScene
});
tween(storyText, {
alpha: 0
}, {
duration: fadeOutDuration,
easing: tween.cubicIn
});
} else {
// If no image/text, just show new scene
fadeInNewScene();
}
}
// Handle tap/click to continue
self.down = function (x, y, obj) {
if (currentScene < scenes.length - 1) {
// Go to next scene
currentScene++;
showScene(currentScene);
} else {
// Fade out story scene
tween(self, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
// Start the main game
startMainGame();
}
}
});
}
};
return self;
});
// TitleScreen class for the game's title screen
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Add background
var bg = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.95
});
// Add title image to fill the whole screen
var titleImg = self.attachAsset('Title', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
// Add animated swirl image, centered, IN FRONT of title
var swirlImg = self.attachAsset('Swirl', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.45
});
self.swirlImg = swirlImg;
// Add heading image above the title image and swirl, so it is in front of both
// Start tiny in the center, then tween to large in the center
var headingImg = self.attachAsset('Heading', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
// Start in the center
scaleX: 0.1,
scaleY: 0.1
});
// Add loading text that shows while assets are loading
var loadingText = new Text2("LOADING...", {
size: 80,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 2048 / 2;
loadingText.y = 2732 / 2 + 600;
loadingText.alpha = 1;
self.addChild(loadingText);
// Pulse the loading text while assets load
var _pulseLoadingText = function pulseLoadingText() {
if (!loadingText || !loadingText.parent) return;
tween(loadingText, {
alpha: 0.3
}, {
duration: 600,
easing: tween.sineInOut,
onFinish: function onFinish() {
if (!loadingText || !loadingText.parent) return;
tween(loadingText, {
alpha: 1
}, {
duration: 600,
easing: tween.sineInOut,
onFinish: _pulseLoadingText
});
}
});
};
_pulseLoadingText();
// Animate heading to grow and stay in the center position, but only after fully loaded
function startTitleScreenAnimations() {
// Fade out loading text first
if (loadingText && loadingText.parent) {
tween(loadingText, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (loadingText && loadingText.parent) {
self.removeChild(loadingText);
}
}
});
}
tween(headingImg, {
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 5000,
easing: tween.cubicOut
});
// Animate swirl rotation using tween for infinite smooth rotation
function startSwirlTween() {
if (!self.swirlImg) return;
// Always rotate to the next full circle (-2*PI) from current rotation for opposite direction
var startRotation = self.swirlImg.rotation || 0;
var endRotation = startRotation - Math.PI * 2;
tween(self.swirlImg, {
rotation: endRotation
}, {
duration: 8000,
easing: tween.linear,
onFinish: function onFinish() {
// Reset rotation to avoid overflow and keep animation smooth
if (self.swirlImg) self.swirlImg.rotation = self.swirlImg.rotation % (Math.PI * 2);
startSwirlTween();
}
});
}
startSwirlTween();
}
// Wait 2 seconds to simulate asset loading delay, then start animations
LK.setTimeout(function () {
if (self && self.parent) {
startTitleScreenAnimations();
}
}, 2000);
// Add "Tap to Start" text with pulsing effect
var startText = new Text2("TAP TO START", {
size: 120,
fill: 0xFFFF00,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 - 300;
self.addChild(startText);
// Pulse the start text
var _pulseStartText = function pulseStartText() {
tween(startText, {
alpha: 0.3
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(startText, {
alpha: 1
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: _pulseStartText
});
}
});
};
_pulseStartText();
// Handle tap/click to start game
self.down = function (x, y, obj) {
// Fade out title screen
tween(self, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
// Start the main game directly
startMainGame();
}
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Global variables that need to be accessible
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var usedInstruments = {};
var puzzle;
var greenTick;
var numberImages = [];
var allClueAssets = [];
var infoButton;
var instructionsScreen = null;
var clueStrikeStates = [false, false];
var usedSpecialInstruments = {};
var usedAnimalImages = {};
var usedFoodImages = {};
var usedToyImages = {};
// Function to start the main game after title screen
function startMainGame() {
// Initialize win/loss counters from storage with defaults
if (typeof storage.wins === 'undefined') storage.wins = 0;
if (typeof storage.losses === 'undefined') storage.losses = 0;
// Reset global variables
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
numberImages = [];
allClueAssets = [];
// Reset clueStrikeStates BEFORE creating clues to prevent old state from being applied
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Add Puzzle to the game when it starts
puzzle = new Puzzle();
game.addChild(puzzle);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Add 1 2 3 4 5 number image assets to the top of the screen
var numberLabelY = 60; // 60px from top
var numberSpacing = 2048 / 6; // 5 numbers, 6 spaces
// Track number image assets for later removal
var numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * numberSpacing,
y: numberLabelY,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// --- CLUE STRIKETHROUGH LOGIC ---
// Track strikethrough state for clues (first two clues only for now)
var clueStrikeStates = [false, false];
// Helper to apply or remove strikethrough effect (alpha fade) for a group of clue assets
function setClueStrikethrough(clueGroup, isStruck) {
var alpha = isStruck ? 0.35 : 1.0;
for (var i = 0; i < clueGroup.length; i++) {
if (clueGroup[i]) clueGroup[i].alpha = alpha;
}
}
// Helper to toggle strikethrough and remember state
// Optionally accepts a clueIndex to persist state for first two clues
function makeClueStrikeToggle(clueGroup, clueIndex) {
var struck = false;
// If restoring, use persisted state
if (typeof clueIndex === "number" && clueStrikeStates[clueIndex] !== undefined) {
struck = clueStrikeStates[clueIndex];
setClueStrikethrough(clueGroup, struck);
}
for (var i = 0; i < clueGroup.length; i++) {
// Attach to all, but only one will handle the toggle
clueGroup[i].down = function () {
struck = !struck;
setClueStrikethrough(clueGroup, struck);
if (typeof clueIndex === "number") {
clueStrikeStates[clueIndex] = struck;
}
};
}
}
// Generate dynamic clues based on the puzzle solution
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Store clue assets globally for cleanup
allClueAssets = clueAssets;
// Add green tick to bottom right corner
var greenTick = LK.getAsset('Greentick', {
anchorX: 1.0,
anchorY: 1.0,
x: 2048 - 0,
//{7i} // moved right by 50px
y: 2732 - 50 - 600 - 35,
// moved up by 15px (previously 10, now 15)
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(greenTick);
// Add info button to bottom left corner
var infoButton = LK.getAsset('Info', {
anchorX: 0.0,
anchorY: 1.0,
x: 0,
y: 2732 - 50 - 600 - 30,
scaleX: 1.5,
scaleY: 1.5
});
game.addChild(infoButton);
// Variable to track instructions screen
var instructionsScreen = null;
// Make info button clickable to show illustrated how-to-play guide
infoButton.down = function (x, y, obj) {
// If instructions screen is already showing, hide it
if (instructionsScreen && instructionsScreen.parent) {
instructionsScreen.parent.removeChild(instructionsScreen);
instructionsScreen = null;
return;
}
// Create instructions screen container
instructionsScreen = new Container();
// Add semi-transparent background
var instructionsBg = LK.getAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.92
});
instructionsScreen.addChild(instructionsBg);
// Add "How to Play" asset to the top of the instructions page
var howToPlayAsset = LK.getAsset('Howtoplaybutton', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 80,
scaleX: 1.0,
scaleY: 1.0
});
instructionsScreen.addChild(howToPlayAsset);
// Add visual clue examples to help players understand the clue system
// Example 1: Equality clue (Sapphire = Chips)
var exampleSapphire = LK.getAsset('Sapphire', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 800,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleSapphire);
var exampleEquals = new Text2("=", {
size: 80,
fill: 0xFFFFFF
});
exampleEquals.anchor.set(0.5, 0.5);
exampleEquals.x = 2048 / 2;
exampleEquals.y = 800;
instructionsScreen.addChild(exampleEquals);
var exampleChips = LK.getAsset('Chips', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 800,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleChips);
// Example 2: Less than clue (Donut < Chips)
var exampleDonut = LK.getAsset('Donut', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 950,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleDonut);
var exampleLessThan = new Text2("<", {
size: 80,
fill: 0xFFFFFF
});
exampleLessThan.anchor.set(0.5, 0.5);
exampleLessThan.x = 2048 / 2;
exampleLessThan.y = 950;
instructionsScreen.addChild(exampleLessThan);
var exampleChips2 = LK.getAsset('Chips', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 950,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleChips2);
// Example 3: Greater than clue (Guitar > Cat)
var exampleGuitar = LK.getAsset('Gutair', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 1100,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleGuitar);
var exampleGreaterThan = new Text2(">", {
size: 80,
fill: 0xFFFFFF
});
exampleGreaterThan.anchor.set(0.5, 0.5);
exampleGreaterThan.x = 2048 / 2;
exampleGreaterThan.y = 1100;
instructionsScreen.addChild(exampleGreaterThan);
var exampleCat = LK.getAsset('Cat', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 1100,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleCat);
// Add step-by-step text instructions positioned below the visual examples
var instructionsText = new Text2("HOW TO PLAY THIS PUZZLE GAME\n\n" + "1. TAP ANY QUESTION MARK to cycle through available pictures\n" + "2. USE THE CLUES (examples shown above):\n" + " • = means 'equals' or 'matches'\n" + " • < means 'comes before' (left to right)\n" + " • > means 'comes after' (left to right)\n\n" + "3. STRIKE THROUGH CLUES by tapping them\n" + "4. CHECK YOUR ANSWER by tapping the green tick\n\n" + "Tap anywhere to close this guide.", {
size: 60,
fill: 0xffffff,
align: 'center',
font: "'Times New Roman Bold','Times New Roman','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 2732 / 2 + 100 + 300;
instructionsScreen.addChild(instructionsText);
// Make the entire instructions screen clickable to close
instructionsScreen.down = function (x, y, obj) {
if (instructionsScreen && instructionsScreen.parent) {
instructionsScreen.parent.removeChild(instructionsScreen);
instructionsScreen = null;
}
};
// Add instructions screen to game
game.addChild(instructionsScreen);
};
// Helper to show/hide info button
function setInfoButtonVisible(visible) {
if (infoButton && infoButton.parent) {
infoButton.visible = !!visible;
}
}
// Function to update clues with strikethrough for solved items (picture clues only)
function updateCluesWithStrikethrough() {
// This function now only handles picture clue strikethrough effects
// The picture clues are already implemented with makeClueStrikeToggle
// No additional text-based clue processing needed
}
// Make green tick clickable to enter puzzle attempt
greenTick.down = function (x, y, obj) {
// Remove all clue assets from game when green tick is clicked
if (typeof allClueAssets === "undefined") allClueAssets = [];
for (var i = 0; i < allClueAssets.length; i++) {
if (allClueAssets[i] && allClueAssets[i].parent) {
allClueAssets[i].parent.removeChild(allClueAssets[i]);
}
}
// Remove number image assets from game when green tick is clicked
if (typeof numberImages !== "undefined" && numberImages.length) {
for (var i = 0; i < numberImages.length; i++) {
if (numberImages[i] && numberImages[i].parent) numberImages[i].parent.removeChild(numberImages[i]);
}
}
if (puzzle && typeof puzzle.checkSolved === "function") {
// Run the check
puzzle.checkSolved();
// If solved, show endpuzzletwo scene
if (puzzle.isSolved) {
// Remove the puzzle and green tick from the game
if (puzzle.parent) puzzle.parent.removeChild(puzzle);
if (greenTick.parent) greenTick.parent.removeChild(greenTick);
// Hide info button when showing endpuzzletwo
setInfoButtonVisible(false);
// Show endpuzzletwo asset centered on screen
var endpuzzletwo = LK.getAsset('Endpuzzletwo', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(endpuzzletwo);
// Fade in endpuzzletwo
tween(endpuzzletwo, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// After 2 seconds, fade out and swap to correct scene with correct word under the correct image
LK.setTimeout(function () {
tween(endpuzzletwo, {
alpha: 0
}, {
duration: 350,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (endpuzzletwo.parent) endpuzzletwo.parent.removeChild(endpuzzletwo);
// Show Correct scene (centered)
var correctScene = LK.getAsset('Correct', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(correctScene);
// Fade in correctScene
tween(correctScene, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// No winning image displayed - just show the correct word
// Increment wins counter
storage.wins = (storage.wins || 0) + 1;
// Show the correct word asset at the bottom of the screen
var correctWord = LK.getAsset('Correctword', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 - 50,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(correctWord);
tween(correctWord, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add win/loss tally text on correct screen
var tallyText = new Text2("WINS: " + storage.wins + " | LOSSES: " + storage.losses, {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
tallyText.anchor.set(0.5, 0.5);
tallyText.x = 2048 / 2;
tallyText.y = 2732 / 2 - 200 - 750;
tallyText.alpha = 0;
game.addChild(tallyText);
tween(tallyText, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add play again button above the correct word
var playAgainBtnY = 2732 - 50 - correctWord.height * 2.2 - 40; // 40px above correct word
var playAgainBtn = LK.getAsset('Playagain', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: playAgainBtnY,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
game.addChild(playAgainBtn);
tween(playAgainBtn, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Play again button handler: remove correct scene and restart puzzle
playAgainBtn.down = function (x, y, obj) {
// Fade out all correct scene assets, then remove and reset
var fadeOuts = [correctScene, correctWord, playAgainBtn, tallyText];
var fadeCount = 0;
var fadeDone = function fadeDone() {
fadeCount++;
if (fadeCount === fadeOuts.length) {
// Remove all correct scene assets
if (correctScene && correctScene.parent) correctScene.parent.removeChild(correctScene);
if (correctWord && correctWord.parent) correctWord.parent.removeChild(correctWord);
if (playAgainBtn && playAgainBtn.parent) playAgainBtn.parent.removeChild(playAgainBtn);
if (tallyText && tallyText.parent) tallyText.parent.removeChild(tallyText);
// Reset all used image trackers so dots can be reused
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
// Reset strikethrough state for all clues BEFORE creating new puzzle
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Re-add puzzle and green tick
puzzle = new Puzzle();
game.addChild(puzzle);
game.addChild(greenTick);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Re-add number image assets to the top of the screen
numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * (2048 / 6),
y: 60,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// Generate new dynamic clues for the new puzzle
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Update global reference
allClueAssets = clueAssets;
// Show info button again after play again
setInfoButtonVisible(true);
}
};
for (var i = 0; i < fadeOuts.length; i++) {
if (fadeOuts[i]) {
tween(fadeOuts[i], {
alpha: 0
}, {
duration: 250,
easing: tween.cubicIn,
onFinish: fadeDone
});
} else {
fadeDone();
}
}
};
}
});
}, 2000);
} else {
// If not solved, show endgame1 scene
if (puzzle.parent) puzzle.parent.removeChild(puzzle);
if (greenTick.parent) greenTick.parent.removeChild(greenTick);
// Hide info button when showing endgame1
setInfoButtonVisible(false);
// Show endgame1 asset centered on screen
var endgame1 = LK.getAsset('Endpuzzleone', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(endgame1);
tween(endgame1, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// After 2 seconds, fade out and swap to incorrect scene with incorrect word under the incorrect image
LK.setTimeout(function () {
tween(endgame1, {
alpha: 0
}, {
duration: 350,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (endgame1.parent) endgame1.parent.removeChild(endgame1);
// Show Incorrect scene (centered)
var incorrectScene = LK.getAsset('Incorrect', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(incorrectScene);
tween(incorrectScene, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Find the first incorrect dot and its expected word
var targetSolution = puzzle.targetSolution || {};
var firstWrongIdx = -1;
var wrongActual = '';
var wrongExpected = '';
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
var expected = targetSolution[i];
var actual = 'Blank';
var row = dot.gridY;
// Get current image based on row and index
if (row === 0 && dot.instruments) {
actual = dot.instruments[dot.instrumentIndex];
} else if (row === 1 && dot.foodImages) {
actual = dot.foodImages[dot.foodIndex];
} else if (row === 2 && dot.toyImages) {
actual = dot.toyImages[dot.toyIndex];
} else if (row === 3 && dot.animalImages) {
actual = dot.animalImages[dot.animalIndex];
} else if (row === 4 && dot.specialInstruments) {
actual = dot.specialInstruments[dot.specialInstrumentIndex];
}
if (actual !== expected) {
firstWrongIdx = i;
wrongActual = actual;
wrongExpected = expected;
break;
}
}
// If found, show the incorrect image and word under it
if (firstWrongIdx !== -1) {
var wrongImg = null;
var imgY = 2732 / 2 + 100;
var wordY = imgY;
// Only show the image if it's not 'Blank'
if (wrongActual !== 'Blank') {
wrongImg = LK.getAsset(wrongActual, {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: imgY,
alpha: 0
});
game.addChild(wrongImg);
tween(wrongImg, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
wordY = 2732 / 2 + 120 + (wrongImg.height || 100); // 20px below image
} else {
// If blank, just show the word lower down
wordY = 2732 / 2 + 120;
}
// Increment losses counter
storage.losses = (storage.losses || 0) + 1;
// Show the incorrect word asset at the bottom of the screen
var incorrectWord = LK.getAsset('Incorrectword', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 - 50,
// 50px padding from bottom
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(incorrectWord);
tween(incorrectWord, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add win/loss tally text on incorrect screen
var incorrectTallyText = new Text2("WINS: " + storage.wins + " | LOSSES: " + storage.losses, {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
incorrectTallyText.anchor.set(0.5, 0.5);
incorrectTallyText.x = 2048 / 2;
incorrectTallyText.y = 2732 / 2 - 200 - 750;
incorrectTallyText.alpha = 0;
game.addChild(incorrectTallyText);
tween(incorrectTallyText, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add retry button below the incorrect image/word
var retryBtnY = 2732 - 50 - incorrectWord.height * 2.2 - 40; // 40px above incorrect word
var retryBtn = LK.getAsset('Retry', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: retryBtnY,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
game.addChild(retryBtn);
tween(retryBtn, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Retry button handler: remove incorrect scene and restart puzzle
retryBtn.down = function (x, y, obj) {
// Fade out all incorrect scene assets, then remove and reset
var fadeOuts = [incorrectScene, wrongImg, incorrectWord, retryBtn, incorrectTallyText];
var fadeCount = 0;
var fadeDone = function fadeDone() {
fadeCount++;
if (fadeCount === fadeOuts.length) {
// Remove all incorrect scene assets
if (incorrectScene && incorrectScene.parent) incorrectScene.parent.removeChild(incorrectScene);
if (wrongImg && wrongImg.parent) wrongImg.parent.removeChild(wrongImg);
if (incorrectWord && incorrectWord.parent) incorrectWord.parent.removeChild(incorrectWord);
if (retryBtn && retryBtn.parent) retryBtn.parent.removeChild(retryBtn);
if (incorrectTallyText && incorrectTallyText.parent) incorrectTallyText.parent.removeChild(incorrectTallyText);
// Reset all used image trackers so dots can be reused
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
// Reset strikethrough state for all clues BEFORE creating new puzzle
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Re-add puzzle and green tick
puzzle = new Puzzle();
game.addChild(puzzle);
game.addChild(greenTick);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Re-add number image assets to the top of the screen
numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * (2048 / 6),
y: 60,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// Generate new dynamic clues for the new puzzle
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Update global reference
allClueAssets = clueAssets;
// Re-add info button after story asset
if (infoButton && infoButton.parent) {
infoButton.parent.removeChild(infoButton);
}
// Find the story asset in the puzzle and add infoButton after it
var storyAssetIdx = -1;
for (var i = 0; i < puzzle.children.length; i++) {
var child = puzzle.children[i];
if (child && child.assetId === 'Story') {
storyAssetIdx = i;
break;
}
}
if (storyAssetIdx !== -1) {
// Insert infoButton after story asset
if (puzzle.children.length > storyAssetIdx + 1) {
puzzle.addChildAt(infoButton, storyAssetIdx + 1);
} else {
puzzle.addChild(infoButton);
}
} else {
// Fallback: add to puzzle if not found
puzzle.addChild(infoButton);
}
// Show info button again after retry
setInfoButtonVisible(true);
}
};
for (var i = 0; i < fadeOuts.length; i++) {
if (fadeOuts[i]) {
tween(fadeOuts[i], {
alpha: 0
}, {
duration: 250,
easing: tween.cubicIn,
onFinish: fadeDone
});
} else {
fadeDone();
}
}
};
}
}
});
}, 2000);
}
}
};
// Update global references that might be needed
window.puzzle = puzzle;
window.greenTick = greenTick;
window.numberImages = numberImages;
window.allClueAssets = allClueAssets;
window.infoButton = infoButton;
}
// Initialize the game with title screen
var titleScreen = new TitleScreen();
game.addChild(titleScreen); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Dot class for interactive dots in the puzzle
var Dot = Container.expand(function () {
var self = Container.call(this);
// Create a visual representation for the dot
var dotGraphics = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
// Dot properties
self.isActive = false;
self.gridX = 0;
self.gridY = 0;
self.instrumentIndex = 0;
self.currentInstrument = null;
// Array of instrument image IDs for first row (dots 1-5)
self.instruments = ['Blank', 'Saxophone', 'Drum', 'Gutair', 'Harp', 'Flute'];
// Array of image IDs for dots 21-25 (bottom row)
self.specialInstruments = ['Blank', 'PJ', 'KJ', 'Harmony', 'Khalida', 'Sapphire'];
// Method to toggle dot state
self.toggle = function () {
self.isActive = !self.isActive;
};
// Method to cycle through instruments for first row (dots 1-5)
self.cycleInstrument = function () {
// Remove current instrument if it exists and free it from usedInstruments
if (self.currentInstrument) {
var currentInstrumentId = self.instruments[self.instrumentIndex];
if (currentInstrumentId !== 'Blank' && usedInstruments[currentInstrumentId] === self) {
delete usedInstruments[currentInstrumentId];
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Find next available instrument
var startIndex = self.instrumentIndex;
var found = false;
do {
self.instrumentIndex = (self.instrumentIndex + 1) % self.instruments.length;
var instrumentId = self.instruments[self.instrumentIndex];
// Check if instrument is available (Blank is always available)
if (instrumentId === 'Blank' || !usedInstruments[instrumentId]) {
found = true;
// Mark non-blank instruments as used
if (instrumentId !== 'Blank') {
usedInstruments[instrumentId] = self;
}
// Create new instrument image
self.currentInstrument = self.attachAsset(instrumentId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
} while (self.instrumentIndex !== startIndex);
// If no available instrument found, stay on current (shouldn't happen with 6 instruments for 5 dots)
if (!found) {
self.instrumentIndex = startIndex;
}
};
// Method to cycle through special images for dots 21-25 (bottom row)
self.cycleSpecialInstrument = function () {
// Remove current instrument if it exists
if (self.currentInstrument) {
// Remove from usedSpecialInstruments if not Blank
if (typeof usedSpecialInstruments !== "undefined" && self.specialInstruments && typeof self.specialInstrumentIndex === "number") {
var prevId = self.specialInstruments[self.specialInstrumentIndex];
if (prevId !== 'Blank' && usedSpecialInstruments[prevId] === self) {
delete usedSpecialInstruments[prevId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Find next available special instrument (no duplicates except Blank)
if (typeof usedSpecialInstruments === "undefined") {
usedSpecialInstruments = {};
}
if (typeof self.specialInstrumentIndex !== "number") self.specialInstrumentIndex = 0;
self.specialInstrumentIndex = typeof self.specialInstrumentIndex === "number" ? self.specialInstrumentIndex : 0;
var startIndex = self.specialInstrumentIndex;
var found = false;
do {
self.specialInstrumentIndex = (self.specialInstrumentIndex + 1) % self.specialInstruments.length;
var instrumentId = self.specialInstruments[self.specialInstrumentIndex];
// Only allow Blank or unused
if (instrumentId === 'Blank' || !usedSpecialInstruments[instrumentId]) {
found = true;
// Mark as used if not Blank
if (instrumentId !== 'Blank') {
usedSpecialInstruments[instrumentId] = self;
}
// Slightly decrease the size of all special images for dots 21-25
var scaleX = 0.85;
var scaleY = 0.85;
if (instrumentId === 'Harmony') {
scaleX = 1.45;
scaleY = 1.45;
}
// Move Khalida or Harmony image up by 10 pixels, move KJ down by 5 pixels
var extraY = 0;
if (instrumentId === 'Khalida' || instrumentId === 'Harmony') {
extraY = -10;
}
if (instrumentId === 'KJ') {
extraY = 5;
}
self.currentInstrument = self.attachAsset(instrumentId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scaleX,
scaleY: scaleY,
y: extraY
});
break;
}
} while (self.specialInstrumentIndex !== startIndex);
// If no available instrument found, stay on current (shouldn't happen)
if (!found) {
self.specialInstrumentIndex = startIndex;
}
};
// Touch/click handler
self.down = function (x, y, obj) {
// Only cycle instruments for dots 1-5 (first row)
if (self.gridY === 0) {
self.cycleInstrument();
}
// Dots 21-25 (bottom row, gridY === 4, gridX 0-4)
else if (self.gridY === 4) {
self.cycleSpecialInstrument();
}
// Dots 16-20 (row 4, gridY === 3, gridX 0-4)
else if (self.gridY === 3) {
// Animal images to cycle through
if (!self.animalImages) {
self.animalImages = ['Blank', 'Cat', 'Dog', 'Fish', 'Frog', 'Turtle'];
self.animalIndex = 0;
}
// Setup usedAnimalImages global tracker if not present
if (typeof usedAnimalImages === "undefined") {
usedAnimalImages = {};
}
// Remove current animal image if exists and update usedAnimalImages
if (self.currentInstrument) {
if (typeof self.animalIndex === "number") {
var prevAnimalId = self.animalImages[self.animalIndex];
if (prevAnimalId !== 'Blank' && usedAnimalImages[prevAnimalId] === self) {
delete usedAnimalImages[prevAnimalId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available animal image (no duplicates except Blank)
var startIndex = typeof self.animalIndex === "number" ? self.animalIndex : 0;
var found = false;
var tries = 0;
do {
self.animalIndex = (typeof self.animalIndex === "number" ? self.animalIndex : 0) + 1;
if (self.animalIndex >= self.animalImages.length) self.animalIndex = 0;
var animalId = self.animalImages[self.animalIndex];
// Only allow Blank or unused
if (animalId === 'Blank' || !usedAnimalImages[animalId]) {
found = true;
// Mark as used if not Blank
if (animalId !== 'Blank') {
usedAnimalImages[animalId] = self;
}
self.currentInstrument = self.attachAsset(animalId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
tries++;
} while (self.animalIndex !== startIndex && tries < self.animalImages.length + 1);
// If no available animal found, stay on current (shouldn't happen)
if (!found) {
self.animalIndex = startIndex;
}
}
// Dots 6-10 (row 2, gridY === 1, gridX 0-4)
else if (self.gridY === 1) {
// Food images to cycle through
if (!self.foodImages) {
self.foodImages = ['Blank', 'Pizza', 'Chips', 'Icecream', 'Donut', 'Spaghetti'];
self.foodIndex = 0;
}
// Setup usedFoodImages global tracker if not present
if (typeof usedFoodImages === "undefined") {
usedFoodImages = {};
}
// Remove current food image if exists and update usedFoodImages
if (self.currentInstrument) {
if (typeof self.foodIndex === "number") {
var prevFoodId = self.foodImages[self.foodIndex];
if (prevFoodId !== 'Blank' && usedFoodImages[prevFoodId] === self) {
delete usedFoodImages[prevFoodId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available food image (no duplicates except Blank)
var startFoodIndex = typeof self.foodIndex === "number" ? self.foodIndex : 0;
var foundFood = false;
var foodTries = 0;
do {
self.foodIndex = (typeof self.foodIndex === "number" ? self.foodIndex : 0) + 1;
if (self.foodIndex >= self.foodImages.length) self.foodIndex = 0;
var foodId = self.foodImages[self.foodIndex];
// Only allow Blank or unused
if (foodId === 'Blank' || !usedFoodImages[foodId]) {
foundFood = true;
// Mark as used if not Blank
if (foodId !== 'Blank') {
usedFoodImages[foodId] = self;
}
self.currentInstrument = self.attachAsset(foodId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
foodTries++;
} while (self.foodIndex !== startFoodIndex && foodTries < self.foodImages.length + 1);
// If no available food found, stay on current (shouldn't happen)
if (!foundFood) {
self.foodIndex = startFoodIndex;
}
} else if (self.gridY === 2) {
// Toy images to cycle through
if (!self.toyImages) {
self.toyImages = ['Blank', 'Lego', 'Dolls', 'Basketball', 'Videogame', 'Plushtoy'];
self.toyIndex = 0;
}
// Setup usedToyImages global tracker if not present
if (typeof usedToyImages === "undefined") {
usedToyImages = {};
}
// Remove current toy image if exists and update usedToyImages
if (self.currentInstrument) {
if (typeof self.toyIndex === "number") {
var prevToyId = self.toyImages[self.toyIndex];
if (prevToyId !== 'Blank' && usedToyImages[prevToyId] === self) {
delete usedToyImages[prevToyId];
}
}
self.removeChild(self.currentInstrument);
self.currentInstrument = null;
}
// Cycle to next available toy image (no duplicates except Blank)
var startToyIndex = typeof self.toyIndex === "number" ? self.toyIndex : 0;
var foundToy = false;
var toyTries = 0;
do {
self.toyIndex = (typeof self.toyIndex === "number" ? self.toyIndex : 0) + 1;
if (self.toyIndex >= self.toyImages.length) self.toyIndex = 0;
var toyId = self.toyImages[self.toyIndex];
// Only allow Blank or unused
if (toyId === 'Blank' || !usedToyImages[toyId]) {
foundToy = true;
// Mark as used if not Blank
if (toyId !== 'Blank') {
usedToyImages[toyId] = self;
}
self.currentInstrument = self.attachAsset(toyId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
break;
}
toyTries++;
} while (self.toyIndex !== startToyIndex && toyTries < self.toyImages.length + 1);
// If no available toy found, stay on current (shouldn't happen)
if (!foundToy) {
self.toyIndex = startToyIndex;
}
}
self.toggle();
// Update clues with strikethrough after changing dot
if (typeof updateCluesWithStrikethrough === 'function') {
updateCluesWithStrikethrough();
}
};
// Initialize dot as inactive
// Initialize with blank instrument
self.currentInstrument = self.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
return self;
});
// Puzzle class for managing a puzzle element in the game
var Puzzle = Container.expand(function () {
var self = Container.call(this);
// Add a 4x5 asset to the puzzle and scale it to be exactly 2048 wide
var targetWidth = 2048;
var assetWidth = 1000;
var scale = targetWidth / assetWidth;
// Center horizontally, align to top
var puzzleImage = self.attachAsset('4x5', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 0,
scaleX: scale,
scaleY: scale
});
// (Story asset removed)
// (Clue images and clue text removed)
// Create 25 dots in a 5x5 grid
self.dots = [];
var gridSize = 5;
var dotSpacing = targetWidth / (gridSize + 1); // Distribute dots evenly across the width
var gridHeight = puzzleImage.height * scale;
var verticalSpacing = gridHeight / (gridSize + 1);
for (var row = 0; row < gridSize; row++) {
for (var col = 0; col < gridSize; col++) {
var dot = new Dot();
dot.gridX = col;
dot.gridY = row;
// Initialize all indices to 0 for consistent reset state
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Position dots over the 4x5 asset
dot.x = (col + 1) * dotSpacing;
dot.y = (row + 1) * verticalSpacing;
self.dots.push(dot);
self.addChild(dot);
}
}
// Generate randomized solution and clues
var generator = new PuzzleGenerator();
self.targetSolution = generator.generateSolution();
self.generatedClues = generator.generateClues(self.targetSolution);
// Example: puzzle state
self.isSolved = false;
// Method to check if puzzle is solved according to target solution
self.checkSolved = function () {
// Use the generated target solution
var targetSolution = self.targetSolution || {};
// Check if all dots match the target solution
var allCorrect = true;
for (var i = 0; i < self.dots.length; i++) {
var dot = self.dots[i];
var expectedImage = targetSolution[i];
// Get current image of the dot based on its row and current index
var currentImage = 'Blank';
var row = dot.gridY;
if (row === 0 && dot.instruments) {
// Row 1: Music instruments
currentImage = dot.instruments[dot.instrumentIndex];
} else if (row === 1 && dot.foodImages) {
// Row 2: Food
currentImage = dot.foodImages[dot.foodIndex];
} else if (row === 2 && dot.toyImages) {
// Row 3: Toys
currentImage = dot.toyImages[dot.toyIndex];
} else if (row === 3 && dot.animalImages) {
// Row 4: Animals
currentImage = dot.animalImages[dot.animalIndex];
} else if (row === 4 && dot.specialInstruments) {
// Row 5: Family
currentImage = dot.specialInstruments[dot.specialInstrumentIndex];
}
// Check if current image matches expected
if (currentImage !== expectedImage) {
allCorrect = false;
break;
}
}
// Update solved state only - win display handled by green tick
if (allCorrect && !self.isSolved) {
self.isSolved = true;
} else if (!allCorrect && self.isSolved) {
self.isSolved = false;
}
};
// Example: update method for per-frame logic
self.update = function () {
// Add per-frame puzzle logic here
// For example, check for completion
self.checkSolved();
};
return self;
});
// PuzzleGenerator class for creating randomized puzzle solutions and clues
var PuzzleGenerator = Container.expand(function () {
var self = Container.call(this);
// Available items for each category (row) - expanded to include all factors
self.categories = {
0: ['Drum', 'Harp', 'Saxophone', 'Flute', 'Gutair'],
// Instruments - matches self.instruments array in Dot class
1: ['Pizza', 'Chips', 'Icecream', 'Donut', 'Spaghetti'],
// Food - matches self.foodImages array in Dot class
2: ['Basketball', 'Dolls', 'Lego', 'Videogame', 'Plushtoy'],
// Toys - matches self.toyImages array in Dot class
3: ['Fish', 'Turtle', 'Frog', 'Cat', 'Dog'],
// Animals - matches self.animalImages array in Dot class
4: ['KJ', 'Khalida', 'Sapphire', 'Harmony', 'PJ'] // People - matches self.specialInstruments array in Dot class
};
// Generate a random valid solution
self.generateSolution = function () {
var solution = {};
// For each row, randomly assign the 5 items to the 5 positions
for (var row = 0; row < 5; row++) {
var items = self.categories[row].slice(); // Copy array
// Shuffle items using Fisher-Yates algorithm
for (var i = items.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = items[i];
items[i] = items[j];
items[j] = temp;
}
// Assign shuffled items to positions
for (var col = 0; col < 5; col++) {
var dotIndex = row * 5 + col;
solution[dotIndex] = items[col];
}
}
return solution;
};
// Generate clues based on the solution
self.generateClues = function (solution) {
var clues = [];
var usedClues = {}; // Track used clues to prevent duplicates
// Validate solution exists and has required data
if (!solution || _typeof2(solution) !== 'object') {
return clues;
}
// Helper function to validate clue items
function isValidItem(item) {
return item && typeof item === 'string' && item !== 'Blank';
}
// Helper function to create unique clue key
function createClueKey(clue) {
if (clue.type === 'equals') {
var items = [clue.item1, clue.item2].sort();
return clue.type + ':' + items[0] + '=' + items[1];
} else if (clue.type === 'order') {
return clue.type + ':' + clue.item1 + clue.operator + clue.item2;
} else if (clue.type === 'position') {
return clue.type + ':' + clue.item1 + '=' + clue.position;
}
return '';
}
// Strategy: Generate more targeted clues that create logical chains
// 1. Start with position clues for anchor points (all columns for better coverage)
var anchorPositions = [0, 1, 2, 3, 4]; // all columns 1-5 for comprehensive coverage
for (var i = 0; i < anchorPositions.length && clues.length < 8; i++) {
var col = anchorPositions[i];
var row = Math.floor(Math.random() * 5);
var item = solution[row * 5 + col];
if (isValidItem(item)) {
var clueCandidate = {
type: 'position',
item1: item,
position: col + 1
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
}
// 2. Generate strategic equality clues - ensure coverage across all columns
var equalityAttempts = 0;
while (clues.length < 15 && equalityAttempts < 50) {
// Distribute equality clues across all columns for better coverage
var col = Math.floor(Math.random() * 5);
var row1 = Math.floor(Math.random() * 5);
var row2;
do {
row2 = Math.floor(Math.random() * 5);
} while (row2 === row1);
var item1 = solution[row1 * 5 + col];
var item2 = solution[row2 * 5 + col];
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'equals',
item1: item1,
item2: item2
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
equalityAttempts++;
}
// 3. Generate ordering clues that create logical connections across all rows
var orderAttempts = 0;
while (clues.length < 22 && orderAttempts < 80) {
var row = Math.floor(Math.random() * 5);
var col1 = Math.floor(Math.random() * 4);
var col2 = col1 + 1;
var item1 = solution[row * 5 + col1];
var item2 = solution[row * 5 + col2];
var operator = '<';
// Check for existing clue connections to prioritize logical chains
var hasExistingClue = false;
for (var c = 0; c < clues.length; c++) {
if (clues[c].item1 === item1 || clues[c].item1 === item2 || clues[c].item2 === item1 || clues[c].item2 === item2) {
hasExistingClue = true;
break;
}
}
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'order',
item1: item1,
item2: item2,
operator: operator
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
// Accept all valid ordering clues to ensure comprehensive coverage
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
orderAttempts++;
}
// 4. Fill remaining slots with additional strategic clues
var fillAttempts = 0;
while (clues.length < 25 && fillAttempts < 30) {
var clueType = Math.random();
if (clueType < 0.4) {
// Additional position clues for remaining columns
var col = Math.floor(Math.random() * 5);
var row = Math.floor(Math.random() * 5);
var item = solution[row * 5 + col];
if (isValidItem(item)) {
var clueCandidate = {
type: 'position',
item1: item,
position: col + 1
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
} else {
// Additional equality clues
var col = Math.floor(Math.random() * 5);
var row1 = Math.floor(Math.random() * 5);
var row2;
do {
row2 = Math.floor(Math.random() * 5);
} while (row2 === row1);
var item1 = solution[row1 * 5 + col];
var item2 = solution[row2 * 5 + col];
if (isValidItem(item1) && isValidItem(item2) && item1 !== item2) {
var clueCandidate = {
type: 'equals',
item1: item1,
item2: item2
};
var clueKey = createClueKey(clueCandidate);
if (!usedClues[clueKey]) {
clues.push(clueCandidate);
usedClues[clueKey] = true;
}
}
}
fillAttempts++;
}
return clues;
};
return self;
});
// StoryScene class for displaying story content
var StoryScene = Container.expand(function () {
var self = Container.call(this);
// Add background
var bg = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.95
});
// Add story content container
var storyContent = new Container();
self.addChild(storyContent);
// --- SCENE DATA ---
var scenes = [{
// First scene
imageId: 'First',
text: "Once upon a time, in a magical puzzle world,\nthere lived characters who needed your help\nto solve their mysteries..."
}, {
// Second scene
imageId: 'Second',
text: "Each puzzle you solve brings harmony to their world.\nAre you ready to begin your adventure?"
}];
var currentScene = 0;
// Add story title
var storyTitle = new Text2("STORY", {
size: 120,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
storyTitle.anchor.set(0.5, 0.5);
storyTitle.x = 2048 / 2;
storyTitle.y = 300;
storyContent.addChild(storyTitle);
// Add story image (centered, fills screen)
var storyImage = storyContent.attachAsset(scenes[0].imageId, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
// Add story text
var storyText = new Text2(scenes[0].text, {
size: 80,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
storyText.anchor.set(0.5, 0.5);
storyText.x = 2048 / 2;
storyText.y = 2732 - 600;
storyContent.addChild(storyText);
// Add continue button
var continueText = new Text2("TAP TO CONTINUE", {
size: 100,
fill: 0xFFFF00,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 2048 / 2;
continueText.y = 2732 - 400;
storyContent.addChild(continueText);
// Pulse the continue text
var _pulseContinueText = function pulseContinueText() {
tween(continueText, {
alpha: 0.3
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(continueText, {
alpha: 1
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: _pulseContinueText
});
}
});
};
_pulseContinueText();
// Helper to update scene visuals with smooth fade transition
function showScene(idx) {
if (idx < 0 || idx >= scenes.length) return;
// Fade out current image and text, then swap and fade in
var fadeOutDuration = 350;
var fadeInDuration = 350;
// Helper to fade in new image and text
function fadeInNewScene() {
// Swap image
if (storyImage && storyImage.parent) {
storyImage.parent.removeChild(storyImage);
}
var newImg = storyContent.attachAsset(scenes[idx].imageId, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
newImg.alpha = 0;
storyImage = newImg;
// Update text
storyText.setText(scenes[idx].text);
storyText.alpha = 0;
// Fade in new image and text
tween(newImg, {
alpha: 1
}, {
duration: fadeInDuration,
easing: tween.cubicOut
});
tween(storyText, {
alpha: 1
}, {
duration: fadeInDuration,
easing: tween.cubicOut
});
}
// Fade out current image and text, then call fadeInNewScene
if (storyImage && storyText) {
tween(storyImage, {
alpha: 0
}, {
duration: fadeOutDuration,
easing: tween.cubicIn,
onFinish: fadeInNewScene
});
tween(storyText, {
alpha: 0
}, {
duration: fadeOutDuration,
easing: tween.cubicIn
});
} else {
// If no image/text, just show new scene
fadeInNewScene();
}
}
// Handle tap/click to continue
self.down = function (x, y, obj) {
if (currentScene < scenes.length - 1) {
// Go to next scene
currentScene++;
showScene(currentScene);
} else {
// Fade out story scene
tween(self, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
// Start the main game
startMainGame();
}
}
});
}
};
return self;
});
// TitleScreen class for the game's title screen
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Add background
var bg = self.attachAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.95
});
// Add title image to fill the whole screen
var titleImg = self.attachAsset('Title', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
});
// Add animated swirl image, centered, IN FRONT of title
var swirlImg = self.attachAsset('Swirl', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.45
});
self.swirlImg = swirlImg;
// Add heading image above the title image and swirl, so it is in front of both
// Start tiny in the center, then tween to large in the center
var headingImg = self.attachAsset('Heading', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
// Start in the center
scaleX: 0.1,
scaleY: 0.1
});
// Add loading text that shows while assets are loading
var loadingText = new Text2("LOADING...", {
size: 80,
fill: 0xFFFFFF,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 2048 / 2;
loadingText.y = 2732 / 2 + 600;
loadingText.alpha = 1;
self.addChild(loadingText);
// Pulse the loading text while assets load
var _pulseLoadingText = function pulseLoadingText() {
if (!loadingText || !loadingText.parent) return;
tween(loadingText, {
alpha: 0.3
}, {
duration: 600,
easing: tween.sineInOut,
onFinish: function onFinish() {
if (!loadingText || !loadingText.parent) return;
tween(loadingText, {
alpha: 1
}, {
duration: 600,
easing: tween.sineInOut,
onFinish: _pulseLoadingText
});
}
});
};
_pulseLoadingText();
// Animate heading to grow and stay in the center position, but only after fully loaded
function startTitleScreenAnimations() {
// Fade out loading text first
if (loadingText && loadingText.parent) {
tween(loadingText, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (loadingText && loadingText.parent) {
self.removeChild(loadingText);
}
}
});
}
tween(headingImg, {
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 5000,
easing: tween.cubicOut
});
// Animate swirl rotation using tween for infinite smooth rotation
function startSwirlTween() {
if (!self.swirlImg) return;
// Always rotate to the next full circle (-2*PI) from current rotation for opposite direction
var startRotation = self.swirlImg.rotation || 0;
var endRotation = startRotation - Math.PI * 2;
tween(self.swirlImg, {
rotation: endRotation
}, {
duration: 8000,
easing: tween.linear,
onFinish: function onFinish() {
// Reset rotation to avoid overflow and keep animation smooth
if (self.swirlImg) self.swirlImg.rotation = self.swirlImg.rotation % (Math.PI * 2);
startSwirlTween();
}
});
}
startSwirlTween();
}
// Wait 2 seconds to simulate asset loading delay, then start animations
LK.setTimeout(function () {
if (self && self.parent) {
startTitleScreenAnimations();
}
}, 2000);
// Add "Tap to Start" text with pulsing effect
var startText = new Text2("TAP TO START", {
size: 120,
fill: 0xFFFF00,
align: 'center',
font: "'Times New Roman','Times New Roman Bold','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 - 300;
self.addChild(startText);
// Pulse the start text
var _pulseStartText = function pulseStartText() {
tween(startText, {
alpha: 0.3
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(startText, {
alpha: 1
}, {
duration: 800,
easing: tween.sineInOut,
onFinish: _pulseStartText
});
}
});
};
_pulseStartText();
// Handle tap/click to start game
self.down = function (x, y, obj) {
// Fade out title screen
tween(self, {
alpha: 0
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
// Start the main game directly
startMainGame();
}
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Global variables that need to be accessible
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var usedInstruments = {};
var puzzle;
var greenTick;
var numberImages = [];
var allClueAssets = [];
var infoButton;
var instructionsScreen = null;
var clueStrikeStates = [false, false];
var usedSpecialInstruments = {};
var usedAnimalImages = {};
var usedFoodImages = {};
var usedToyImages = {};
// Function to start the main game after title screen
function startMainGame() {
// Initialize win/loss counters from storage with defaults
if (typeof storage.wins === 'undefined') storage.wins = 0;
if (typeof storage.losses === 'undefined') storage.losses = 0;
// Reset global variables
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
numberImages = [];
allClueAssets = [];
// Reset clueStrikeStates BEFORE creating clues to prevent old state from being applied
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Add Puzzle to the game when it starts
puzzle = new Puzzle();
game.addChild(puzzle);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Add 1 2 3 4 5 number image assets to the top of the screen
var numberLabelY = 60; // 60px from top
var numberSpacing = 2048 / 6; // 5 numbers, 6 spaces
// Track number image assets for later removal
var numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * numberSpacing,
y: numberLabelY,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// --- CLUE STRIKETHROUGH LOGIC ---
// Track strikethrough state for clues (first two clues only for now)
var clueStrikeStates = [false, false];
// Helper to apply or remove strikethrough effect (alpha fade) for a group of clue assets
function setClueStrikethrough(clueGroup, isStruck) {
var alpha = isStruck ? 0.35 : 1.0;
for (var i = 0; i < clueGroup.length; i++) {
if (clueGroup[i]) clueGroup[i].alpha = alpha;
}
}
// Helper to toggle strikethrough and remember state
// Optionally accepts a clueIndex to persist state for first two clues
function makeClueStrikeToggle(clueGroup, clueIndex) {
var struck = false;
// If restoring, use persisted state
if (typeof clueIndex === "number" && clueStrikeStates[clueIndex] !== undefined) {
struck = clueStrikeStates[clueIndex];
setClueStrikethrough(clueGroup, struck);
}
for (var i = 0; i < clueGroup.length; i++) {
// Attach to all, but only one will handle the toggle
clueGroup[i].down = function () {
struck = !struck;
setClueStrikethrough(clueGroup, struck);
if (typeof clueIndex === "number") {
clueStrikeStates[clueIndex] = struck;
}
};
}
}
// Generate dynamic clues based on the puzzle solution
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Store clue assets globally for cleanup
allClueAssets = clueAssets;
// Add green tick to bottom right corner
var greenTick = LK.getAsset('Greentick', {
anchorX: 1.0,
anchorY: 1.0,
x: 2048 - 0,
//{7i} // moved right by 50px
y: 2732 - 50 - 600 - 35,
// moved up by 15px (previously 10, now 15)
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(greenTick);
// Add info button to bottom left corner
var infoButton = LK.getAsset('Info', {
anchorX: 0.0,
anchorY: 1.0,
x: 0,
y: 2732 - 50 - 600 - 30,
scaleX: 1.5,
scaleY: 1.5
});
game.addChild(infoButton);
// Variable to track instructions screen
var instructionsScreen = null;
// Make info button clickable to show illustrated how-to-play guide
infoButton.down = function (x, y, obj) {
// If instructions screen is already showing, hide it
if (instructionsScreen && instructionsScreen.parent) {
instructionsScreen.parent.removeChild(instructionsScreen);
instructionsScreen = null;
return;
}
// Create instructions screen container
instructionsScreen = new Container();
// Add semi-transparent background
var instructionsBg = LK.getAsset('Street', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.92
});
instructionsScreen.addChild(instructionsBg);
// Add "How to Play" asset to the top of the instructions page
var howToPlayAsset = LK.getAsset('Howtoplaybutton', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 80,
scaleX: 1.0,
scaleY: 1.0
});
instructionsScreen.addChild(howToPlayAsset);
// Add visual clue examples to help players understand the clue system
// Example 1: Equality clue (Sapphire = Chips)
var exampleSapphire = LK.getAsset('Sapphire', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 800,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleSapphire);
var exampleEquals = new Text2("=", {
size: 80,
fill: 0xFFFFFF
});
exampleEquals.anchor.set(0.5, 0.5);
exampleEquals.x = 2048 / 2;
exampleEquals.y = 800;
instructionsScreen.addChild(exampleEquals);
var exampleChips = LK.getAsset('Chips', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 800,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleChips);
// Example 2: Less than clue (Donut < Chips)
var exampleDonut = LK.getAsset('Donut', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 950,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleDonut);
var exampleLessThan = new Text2("<", {
size: 80,
fill: 0xFFFFFF
});
exampleLessThan.anchor.set(0.5, 0.5);
exampleLessThan.x = 2048 / 2;
exampleLessThan.y = 950;
instructionsScreen.addChild(exampleLessThan);
var exampleChips2 = LK.getAsset('Chips', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 950,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleChips2);
// Example 3: Greater than clue (Guitar > Cat)
var exampleGuitar = LK.getAsset('Gutair', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 1100,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleGuitar);
var exampleGreaterThan = new Text2(">", {
size: 80,
fill: 0xFFFFFF
});
exampleGreaterThan.anchor.set(0.5, 0.5);
exampleGreaterThan.x = 2048 / 2;
exampleGreaterThan.y = 1100;
instructionsScreen.addChild(exampleGreaterThan);
var exampleCat = LK.getAsset('Cat', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 1100,
scaleX: 0.6,
scaleY: 0.6
});
instructionsScreen.addChild(exampleCat);
// Add step-by-step text instructions positioned below the visual examples
var instructionsText = new Text2("HOW TO PLAY THIS PUZZLE GAME\n\n" + "1. TAP ANY QUESTION MARK to cycle through available pictures\n" + "2. USE THE CLUES (examples shown above):\n" + " • = means 'equals' or 'matches'\n" + " • < means 'comes before' (left to right)\n" + " • > means 'comes after' (left to right)\n\n" + "3. STRIKE THROUGH CLUES by tapping them\n" + "4. CHECK YOUR ANSWER by tapping the green tick\n\n" + "Tap anywhere to close this guide.", {
size: 60,
fill: 0xffffff,
align: 'center',
font: "'Times New Roman Bold','Times New Roman','GillSans-Bold',Impact,'Arial Black',Tahoma"
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 2732 / 2 + 100 + 300;
instructionsScreen.addChild(instructionsText);
// Make the entire instructions screen clickable to close
instructionsScreen.down = function (x, y, obj) {
if (instructionsScreen && instructionsScreen.parent) {
instructionsScreen.parent.removeChild(instructionsScreen);
instructionsScreen = null;
}
};
// Add instructions screen to game
game.addChild(instructionsScreen);
};
// Helper to show/hide info button
function setInfoButtonVisible(visible) {
if (infoButton && infoButton.parent) {
infoButton.visible = !!visible;
}
}
// Function to update clues with strikethrough for solved items (picture clues only)
function updateCluesWithStrikethrough() {
// This function now only handles picture clue strikethrough effects
// The picture clues are already implemented with makeClueStrikeToggle
// No additional text-based clue processing needed
}
// Make green tick clickable to enter puzzle attempt
greenTick.down = function (x, y, obj) {
// Remove all clue assets from game when green tick is clicked
if (typeof allClueAssets === "undefined") allClueAssets = [];
for (var i = 0; i < allClueAssets.length; i++) {
if (allClueAssets[i] && allClueAssets[i].parent) {
allClueAssets[i].parent.removeChild(allClueAssets[i]);
}
}
// Remove number image assets from game when green tick is clicked
if (typeof numberImages !== "undefined" && numberImages.length) {
for (var i = 0; i < numberImages.length; i++) {
if (numberImages[i] && numberImages[i].parent) numberImages[i].parent.removeChild(numberImages[i]);
}
}
if (puzzle && typeof puzzle.checkSolved === "function") {
// Run the check
puzzle.checkSolved();
// If solved, show endpuzzletwo scene
if (puzzle.isSolved) {
// Remove the puzzle and green tick from the game
if (puzzle.parent) puzzle.parent.removeChild(puzzle);
if (greenTick.parent) greenTick.parent.removeChild(greenTick);
// Hide info button when showing endpuzzletwo
setInfoButtonVisible(false);
// Show endpuzzletwo asset centered on screen
var endpuzzletwo = LK.getAsset('Endpuzzletwo', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(endpuzzletwo);
// Fade in endpuzzletwo
tween(endpuzzletwo, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// After 2 seconds, fade out and swap to correct scene with correct word under the correct image
LK.setTimeout(function () {
tween(endpuzzletwo, {
alpha: 0
}, {
duration: 350,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (endpuzzletwo.parent) endpuzzletwo.parent.removeChild(endpuzzletwo);
// Show Correct scene (centered)
var correctScene = LK.getAsset('Correct', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(correctScene);
// Fade in correctScene
tween(correctScene, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// No winning image displayed - just show the correct word
// Increment wins counter
storage.wins = (storage.wins || 0) + 1;
// Show the correct word asset at the bottom of the screen
var correctWord = LK.getAsset('Correctword', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 - 50,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(correctWord);
tween(correctWord, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add win/loss tally text on correct screen
var tallyText = new Text2("WINS: " + storage.wins + " | LOSSES: " + storage.losses, {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
tallyText.anchor.set(0.5, 0.5);
tallyText.x = 2048 / 2;
tallyText.y = 2732 / 2 - 200 - 750;
tallyText.alpha = 0;
game.addChild(tallyText);
tween(tallyText, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add play again button above the correct word
var playAgainBtnY = 2732 - 50 - correctWord.height * 2.2 - 40; // 40px above correct word
var playAgainBtn = LK.getAsset('Playagain', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: playAgainBtnY,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
game.addChild(playAgainBtn);
tween(playAgainBtn, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Play again button handler: remove correct scene and restart puzzle
playAgainBtn.down = function (x, y, obj) {
// Fade out all correct scene assets, then remove and reset
var fadeOuts = [correctScene, correctWord, playAgainBtn, tallyText];
var fadeCount = 0;
var fadeDone = function fadeDone() {
fadeCount++;
if (fadeCount === fadeOuts.length) {
// Remove all correct scene assets
if (correctScene && correctScene.parent) correctScene.parent.removeChild(correctScene);
if (correctWord && correctWord.parent) correctWord.parent.removeChild(correctWord);
if (playAgainBtn && playAgainBtn.parent) playAgainBtn.parent.removeChild(playAgainBtn);
if (tallyText && tallyText.parent) tallyText.parent.removeChild(tallyText);
// Reset all used image trackers so dots can be reused
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
// Reset strikethrough state for all clues BEFORE creating new puzzle
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Re-add puzzle and green tick
puzzle = new Puzzle();
game.addChild(puzzle);
game.addChild(greenTick);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Re-add number image assets to the top of the screen
numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * (2048 / 6),
y: 60,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// Generate new dynamic clues for the new puzzle
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Update global reference
allClueAssets = clueAssets;
// Show info button again after play again
setInfoButtonVisible(true);
}
};
for (var i = 0; i < fadeOuts.length; i++) {
if (fadeOuts[i]) {
tween(fadeOuts[i], {
alpha: 0
}, {
duration: 250,
easing: tween.cubicIn,
onFinish: fadeDone
});
} else {
fadeDone();
}
}
};
}
});
}, 2000);
} else {
// If not solved, show endgame1 scene
if (puzzle.parent) puzzle.parent.removeChild(puzzle);
if (greenTick.parent) greenTick.parent.removeChild(greenTick);
// Hide info button when showing endgame1
setInfoButtonVisible(false);
// Show endgame1 asset centered on screen
var endgame1 = LK.getAsset('Endpuzzleone', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(endgame1);
tween(endgame1, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// After 2 seconds, fade out and swap to incorrect scene with incorrect word under the incorrect image
LK.setTimeout(function () {
tween(endgame1, {
alpha: 0
}, {
duration: 350,
easing: tween.cubicIn,
onFinish: function onFinish() {
if (endgame1.parent) endgame1.parent.removeChild(endgame1);
// Show Incorrect scene (centered)
var incorrectScene = LK.getAsset('Incorrect', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(incorrectScene);
tween(incorrectScene, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Find the first incorrect dot and its expected word
var targetSolution = puzzle.targetSolution || {};
var firstWrongIdx = -1;
var wrongActual = '';
var wrongExpected = '';
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
var expected = targetSolution[i];
var actual = 'Blank';
var row = dot.gridY;
// Get current image based on row and index
if (row === 0 && dot.instruments) {
actual = dot.instruments[dot.instrumentIndex];
} else if (row === 1 && dot.foodImages) {
actual = dot.foodImages[dot.foodIndex];
} else if (row === 2 && dot.toyImages) {
actual = dot.toyImages[dot.toyIndex];
} else if (row === 3 && dot.animalImages) {
actual = dot.animalImages[dot.animalIndex];
} else if (row === 4 && dot.specialInstruments) {
actual = dot.specialInstruments[dot.specialInstrumentIndex];
}
if (actual !== expected) {
firstWrongIdx = i;
wrongActual = actual;
wrongExpected = expected;
break;
}
}
// If found, show the incorrect image and word under it
if (firstWrongIdx !== -1) {
var wrongImg = null;
var imgY = 2732 / 2 + 100;
var wordY = imgY;
// Only show the image if it's not 'Blank'
if (wrongActual !== 'Blank') {
wrongImg = LK.getAsset(wrongActual, {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: imgY,
alpha: 0
});
game.addChild(wrongImg);
tween(wrongImg, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
wordY = 2732 / 2 + 120 + (wrongImg.height || 100); // 20px below image
} else {
// If blank, just show the word lower down
wordY = 2732 / 2 + 120;
}
// Increment losses counter
storage.losses = (storage.losses || 0) + 1;
// Show the incorrect word asset at the bottom of the screen
var incorrectWord = LK.getAsset('Incorrectword', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 - 50,
// 50px padding from bottom
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
});
game.addChild(incorrectWord);
tween(incorrectWord, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add win/loss tally text on incorrect screen
var incorrectTallyText = new Text2("WINS: " + storage.wins + " | LOSSES: " + storage.losses, {
size: 80,
fill: 0xFFFFFF,
align: 'center'
});
incorrectTallyText.anchor.set(0.5, 0.5);
incorrectTallyText.x = 2048 / 2;
incorrectTallyText.y = 2732 / 2 - 200 - 750;
incorrectTallyText.alpha = 0;
game.addChild(incorrectTallyText);
tween(incorrectTallyText, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Add retry button below the incorrect image/word
var retryBtnY = 2732 - 50 - incorrectWord.height * 2.2 - 40; // 40px above incorrect word
var retryBtn = LK.getAsset('Retry', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: retryBtnY,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0
});
game.addChild(retryBtn);
tween(retryBtn, {
alpha: 1
}, {
duration: 350,
easing: tween.cubicOut
});
// Retry button handler: remove incorrect scene and restart puzzle
retryBtn.down = function (x, y, obj) {
// Fade out all incorrect scene assets, then remove and reset
var fadeOuts = [incorrectScene, wrongImg, incorrectWord, retryBtn, incorrectTallyText];
var fadeCount = 0;
var fadeDone = function fadeDone() {
fadeCount++;
if (fadeCount === fadeOuts.length) {
// Remove all incorrect scene assets
if (incorrectScene && incorrectScene.parent) incorrectScene.parent.removeChild(incorrectScene);
if (wrongImg && wrongImg.parent) wrongImg.parent.removeChild(wrongImg);
if (incorrectWord && incorrectWord.parent) incorrectWord.parent.removeChild(incorrectWord);
if (retryBtn && retryBtn.parent) retryBtn.parent.removeChild(retryBtn);
if (incorrectTallyText && incorrectTallyText.parent) incorrectTallyText.parent.removeChild(incorrectTallyText);
// Reset all used image trackers so dots can be reused
usedInstruments = {};
usedSpecialInstruments = {};
usedAnimalImages = {};
usedFoodImages = {};
usedToyImages = {};
// Reset strikethrough state for all clues BEFORE creating new puzzle
clueStrikeStates = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
// Re-add puzzle and green tick
puzzle = new Puzzle();
game.addChild(puzzle);
game.addChild(greenTick);
// Reset all dots to ensure clean state
for (var i = 0; i < puzzle.dots.length; i++) {
var dot = puzzle.dots[i];
// Reset all indices to 0
dot.instrumentIndex = 0;
dot.specialInstrumentIndex = 0;
dot.animalIndex = 0;
dot.foodIndex = 0;
dot.toyIndex = 0;
// Clear current instrument and reset to blank
if (dot.currentInstrument) {
dot.removeChild(dot.currentInstrument);
dot.currentInstrument = null;
}
// Set to blank instrument
dot.currentInstrument = dot.attachAsset('Blank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
}
// Re-add number image assets to the top of the screen
numberImages = [];
for (var i = 1; i <= 5; i++) {
var numImg = LK.getAsset(i + "", {
anchorX: 0.5,
anchorY: 0,
x: i * (2048 / 6),
y: 60,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(numImg);
numberImages.push(numImg);
}
// Generate new dynamic clues for the new puzzle
var clueAssets = [];
var clueY = 2732 - 40 - 500 + 30;
var clueX = 30;
var clueSpacing = 120;
var clueIndex = 0;
// Create visual clues from generated clue data
for (var i = 0; i < puzzle.generatedClues.length && i < 25; i++) {
var clue = puzzle.generatedClues[i];
// Skip clue if it has invalid properties
if (!clue || !clue.item1) {
continue;
}
var clueAssetGroup = [];
// Calculate position (5 columns of 5 clues each)
// Adjust column spacing to fit within screen width (2048px)
var columnSpacing = (2048 - clueX * 2) / 5; // Distribute 5 columns evenly across available width
var columnX = clueX + Math.floor(clueIndex / 5) * columnSpacing;
var currentY = clueY + clueIndex % 5 * clueSpacing;
// Create first item with proper scaling for special items
var item1Scale = 0.45;
var item1ExtraY = 0;
if (clue.item1 === 'Harmony') {
item1Scale = 0.55;
item1ExtraY = -10;
} else if (clue.item1 === 'Khalida') {
item1ExtraY = -10;
} else if (clue.item1 === 'KJ') {
item1ExtraY = 5;
}
var item1Asset = LK.getAsset(clue.item1, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX,
y: currentY + item1ExtraY,
scaleX: item1Scale,
scaleY: item1Scale
});
game.addChild(item1Asset);
clueAssetGroup.push(item1Asset);
// Create operator text
var operatorText;
if (clue.type === 'equals') {
operatorText = "=";
} else if (clue.type === 'order') {
operatorText = clue.operator;
} else if (clue.type === 'position') {
operatorText = "=";
}
var opText = new Text2(operatorText, {
size: 120,
fill: 0xFFFFFF
});
opText.anchor.set(0.5, 1.0);
opText.x = columnX + 200 * 0.45 + 30;
opText.y = currentY;
game.addChild(opText);
clueAssetGroup.push(opText);
// Create second item
var secondItem;
var item2Scale = 0.45;
var item2ExtraY = 0;
if (clue.type === 'position') {
secondItem = clue.position.toString();
} else {
secondItem = clue.item2;
// Apply special scaling for second item too
if (secondItem && secondItem === 'Harmony') {
item2Scale = 0.55;
item2ExtraY = -10;
} else if (secondItem && secondItem === 'Khalida') {
item2ExtraY = -10;
} else if (secondItem && secondItem === 'KJ') {
item2ExtraY = 5;
}
}
// Only create second item if it exists
if (secondItem) {
var item2Asset = LK.getAsset(secondItem, {
anchorX: 0.0,
anchorY: 1.0,
x: columnX + 200 * 0.45 + 60,
y: currentY + item2ExtraY,
scaleX: item2Scale,
scaleY: item2Scale
});
game.addChild(item2Asset);
clueAssetGroup.push(item2Asset);
}
// Add strikethrough functionality
makeClueStrikeToggle(clueAssetGroup, i < 2 ? i : undefined);
// Store for later cleanup
clueAssets = clueAssets.concat(clueAssetGroup);
clueIndex++;
}
// Update global reference
allClueAssets = clueAssets;
// Re-add info button after story asset
if (infoButton && infoButton.parent) {
infoButton.parent.removeChild(infoButton);
}
// Find the story asset in the puzzle and add infoButton after it
var storyAssetIdx = -1;
for (var i = 0; i < puzzle.children.length; i++) {
var child = puzzle.children[i];
if (child && child.assetId === 'Story') {
storyAssetIdx = i;
break;
}
}
if (storyAssetIdx !== -1) {
// Insert infoButton after story asset
if (puzzle.children.length > storyAssetIdx + 1) {
puzzle.addChildAt(infoButton, storyAssetIdx + 1);
} else {
puzzle.addChild(infoButton);
}
} else {
// Fallback: add to puzzle if not found
puzzle.addChild(infoButton);
}
// Show info button again after retry
setInfoButtonVisible(true);
}
};
for (var i = 0; i < fadeOuts.length; i++) {
if (fadeOuts[i]) {
tween(fadeOuts[i], {
alpha: 0
}, {
duration: 250,
easing: tween.cubicIn,
onFinish: fadeDone
});
} else {
fadeDone();
}
}
};
}
}
});
}, 2000);
}
}
};
// Update global references that might be needed
window.puzzle = puzzle;
window.greenTick = greenTick;
window.numberImages = numberImages;
window.allClueAssets = allClueAssets;
window.infoButton = infoButton;
}
// Initialize the game with title screen
var titleScreen = new TitleScreen();
game.addChild(titleScreen);
Add more vibrant colours to picture
Make this scene more modern like in the present
A 4x5 grid in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Remove man
Saxophone in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Harp in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Drum in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Flute in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Question mark professor Layton game style. In-Game asset. 2d. High contrast. No shadows
12yo blonde girl in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Cute little10yo girl brown hair in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Cute little 7yo girl with blonde curly hair. Professor Layton game style In-Game asset. 2d. High contrast. No shadows
15 yo boy with short scruffy blonde hair professor Layton game style. In-Game asset. 2d. High contrast. No shadows
18yo girl with short brown hair professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Cat in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
White dog with brown patch on eyes professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Turtle in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Frog in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Goldfish in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Basketball ball professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Lego bricks professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Video game console professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Teddy bear professor Layton game style. In-Game asset. 2d. High contrast. No shadows
These dolls in professor Layton game art style
Hot chips or fries in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Bowl of spaghetti in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Pizza in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Chocolate donut in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Ice cream cone in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Make her crack a small smile
The word "correct" in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
The word " incorrect" in professor Layton game style. In-Game asset. 2d. High contrast. No shadows
Green tick in professor Layton gamestyle. In-Game asset. 2d. High contrast. No shadows
Button with RETRY PUZZLE on it in professor Layton game style artwork In-Game asset. 2d. High contrast. No shadows
Information symbol in professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Number 1 button professor Layton game style artwork. In-Game asset. 2d. High contrast. No shadows
Number 2
Number 3
Number 4
Number 5
Remove clock
Make sure J and Y is not cut off
How to play button in professor Layton game style font. In-Game asset. 2d. High contrast. No shadows
Make robe hang lower so you can't see it's feet
Make it say play new puzzle
A 16:9 title banner