/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var Character = Container.expand(function () { var self = Container.call(this); // Character properties self.isHidden = true; self.isFound = false; self.peekFrequency = 0; self.peekDuration = 0; self.soundFrequency = 0; self.lastPeekTime = 0; self.lastSoundTime = 0; self.requiresMouthOpen = false; self.requiresSound = false; // Create character visual var characterGraphics = self.attachAsset('hiddenCharacter', { anchorX: 0.5, anchorY: 0.5, alpha: 0.4 }); // Create found effect (initially invisible) var foundEffect = self.attachAsset('foundEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); // Setup character with specific behavior self.setup = function (config) { self.peekFrequency = config.peekFrequency || 0; self.peekDuration = config.peekDuration || 0; self.soundFrequency = config.soundFrequency || 0; self.requiresMouthOpen = config.requiresMouthOpen || false; self.requiresSound = config.requiresSound || false; // Set initial state if (self.peekFrequency === 0) { characterGraphics.alpha = 0.3; // Always slightly visible } else { characterGraphics.alpha = 0; // Hide initially } }; // Check if the character is currently revealing itself self.update = function () { // Skip if already found if (self.isFound) { return; } // Handle peek behavior if (self.peekFrequency > 0) { var currentTime = Date.now(); // Time to start a new peek if (currentTime - self.lastPeekTime > self.peekFrequency) { self.lastPeekTime = currentTime; characterGraphics.alpha = 0.5; // Set timer to hide again LK.setTimeout(function () { if (!self.isFound) { characterGraphics.alpha = 0; } }, self.peekDuration); } } // Handle sound behavior if (self.soundFrequency > 0) { var currentTime = Date.now(); // Time to make a sound if (currentTime - self.lastSoundTime > self.soundFrequency) { self.lastSoundTime = currentTime; LK.getSound('characterSound').play(); } } }; // Check if player can find this character self.checkFindCondition = function () { if (self.isFound) { return false; } var canFind = true; // Check for mouth open requirement if (self.requiresMouthOpen && !facekit.mouthOpen) { canFind = false; } // Check for sound requirement if (self.requiresSound && facekit.volume < 0.3) { canFind = false; } return canFind; }; // Mark as found and show animation self.findCharacter = function () { if (self.isFound) { return; } self.isFound = true; characterGraphics.alpha = 1; // Switch to found appearance tween(characterGraphics, { tint: 0x22aa22 }, { duration: 500 }); // Show found effect foundEffect.alpha = 0.7; tween(foundEffect, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1000 }); // Play sound LK.getSound('found').play(); return true; }; return self; }); var TimerBar = Container.expand(function () { var self = Container.call(this); // Create background bar var barBg = self.attachAsset('timerBg', { anchorX: 0, anchorY: 0.5 }); // Create timer bar var bar = self.attachAsset('timerBar', { anchorX: 0, anchorY: 0.5 }); // Properties self.maxTime = 60; // seconds self.currentTime = 60; self.warningThreshold = 10; // seconds self.hasWarnedLowTime = false; // Initialize with time self.setTime = function (time) { self.maxTime = time; self.currentTime = time; self.updateVisual(); }; // Update time remaining self.updateTime = function (delta) { self.currentTime -= delta; if (self.currentTime < 0) { self.currentTime = 0; } // Check for low time warning if (self.currentTime <= self.warningThreshold && !self.hasWarnedLowTime) { self.hasWarnedLowTime = true; LK.getSound('timeWarn').play(); tween(bar, { tint: 0xff0000 }, { duration: 500 }); } self.updateVisual(); return self.currentTime <= 0; }; // Update visual representation self.updateVisual = function () { var percentage = self.currentTime / self.maxTime; bar.scale.x = percentage; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state variables var level = 1; var charactersFound = 0; var totalCharacters = 0; var gameActive = false; var lastTime = Date.now(); var timeSinceLastTick = 0; var characters = []; var timerBar; var levelTime = 60; // seconds var isLevelComplete = false; // UI Elements var scoreTxt; var levelTxt; var findTxt; var instructionTxt; // Initialize game function initGame() { // Set up UI setupUI(); // Start game with level 1 startLevel(level); // Start background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } // Setup UI elements function setupUI() { // Create score text scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); LK.gui.topRight.addChild(scoreTxt); // Create level text levelTxt = new Text2('Level: 1', { size: 80, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.topRight.addChild(levelTxt); levelTxt.y = 100; // Create find text (shows how many characters left to find) findTxt = new Text2('Find: 0/0', { size: 80, fill: 0xFFFFFF }); findTxt.anchor.set(0.5, 0); LK.gui.top.addChild(findTxt); // Create instruction text instructionTxt = new Text2('Find the hidden characters!', { size: 60, fill: 0xFFFFFF }); instructionTxt.anchor.set(0.5, 1); LK.gui.bottom.addChild(instructionTxt); // Create timer bar timerBar = new TimerBar(); timerBar.x = 2048 / 2 - 900; // Center timerBar.y = 120; LK.gui.top.addChild(timerBar); } // Start a new level function startLevel(levelNum) { // Clean up previous level cleanupLevel(); // Update variables level = levelNum; charactersFound = 0; isLevelComplete = false; // Set level time (decreases with each level) levelTime = Math.max(30, 60 - (level - 1) * 5); // Update UI levelTxt.setText('Level: ' + level); // Calculate number of characters for this level totalCharacters = 3 + Math.min(7, Math.floor(level / 2)); // Update find text findTxt.setText('Find: 0/' + totalCharacters); // Show appropriate instructions if (level === 1) { instructionTxt.setText('Look around to find the hidden characters!'); } else if (level === 2) { instructionTxt.setText('Some characters need you to open your mouth!'); } else if (level === 3) { instructionTxt.setText('Try making sounds to find some characters!'); } else { instructionTxt.setText('Use all your skills to find the hidden characters!'); } // Create characters createCharacters(); // Reset timer timerBar.setTime(levelTime); timerBar.hasWarnedLowTime = false; // Start the game gameActive = true; lastTime = Date.now(); } // Create characters for the current level function createCharacters() { // Clear any existing characters characters = []; for (var i = 0; i < totalCharacters; i++) { var character = new Character(); // Position randomly on screen character.x = 200 + Math.random() * (2048 - 400); character.y = 200 + Math.random() * (2732 - 400); // Determine character behavior based on level and position in array var config = { peekFrequency: 0, peekDuration: 0, soundFrequency: 0, requiresMouthOpen: false, requiresSound: false }; // Different character behaviors based on index and level if (i < totalCharacters / 3) { // Basic characters - just peek occasionally config.peekFrequency = 3000 + Math.random() * 5000; config.peekDuration = 500 + Math.random() * 1000; } else if (i < totalCharacters * 2 / 3) { // Medium characters - require mouth to be open if (level >= 2) { config.requiresMouthOpen = true; config.peekFrequency = 4000 + Math.random() * 6000; config.peekDuration = 400 + Math.random() * 800; } else { config.peekFrequency = 3000 + Math.random() * 5000; config.peekDuration = 500 + Math.random() * 1000; } } else { // Advanced characters - require sound if (level >= 3) { config.requiresSound = true; config.soundFrequency = 7000 + Math.random() * 10000; } else { config.peekFrequency = 4000 + Math.random() * 6000; config.peekDuration = 300 + Math.random() * 600; } } // Setup character character.setup(config); // Add to game and tracking array game.addChild(character); characters.push(character); } } // Clean up the current level function cleanupLevel() { // Remove all characters for (var i = 0; i < characters.length; i++) { if (characters[i].parent) { characters[i].parent.removeChild(characters[i]); } } characters = []; } // Update game state game.update = function () { // Skip if game not active if (!gameActive) { return; } // Calculate time delta var currentTime = Date.now(); var delta = (currentTime - lastTime) / 1000; // Convert to seconds lastTime = currentTime; // Update timer var timeUp = timerBar.updateTime(delta); if (timeUp && !isLevelComplete) { gameOver(); return; } // Update each character for (var i = 0; i < characters.length; i++) { characters[i].update(); } }; // Handle touch/click on game game.down = function (x, y, obj) { // Skip if game not active if (!gameActive) { return; } // Check each character to see if clicked for (var i = 0; i < characters.length; i++) { var character = characters[i]; // Check if touch is on this character if (x >= character.x - 100 && x <= character.x + 100 && y >= character.y - 100 && y <= character.y + 100) { // Check if this character can be found based on conditions if (character.checkFindCondition()) { // Find the character if (character.findCharacter()) { // Update found count charactersFound++; // Update UI findTxt.setText('Find: ' + charactersFound + '/' + totalCharacters); // Add score var scoreGain = 100 + Math.floor(timerBar.currentTime * 10); LK.setScore(LK.getScore() + scoreGain); scoreTxt.setText('Score: ' + LK.getScore()); // Check if level complete if (charactersFound >= totalCharacters) { levelComplete(); } } } } } }; // Handle move events game.move = function (x, y, obj) { // Game uses camera, no specific move handling needed }; // Level complete function levelComplete() { isLevelComplete = true; gameActive = false; // Show level complete message instructionTxt.setText('Level ' + level + ' Complete! Starting next level...'); // Wait a moment before starting next level LK.setTimeout(function () { startLevel(level + 1); }, 3000); } // Game over function gameOver() { gameActive = false; // Show game over message instructionTxt.setText('Time\'s up! Game Over!'); // Short delay before showing game over screen LK.setTimeout(function () { LK.showGameOver(); }, 2000); } // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
var facekit = LK.import("@upit/facekit.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
// Character properties
self.isHidden = true;
self.isFound = false;
self.peekFrequency = 0;
self.peekDuration = 0;
self.soundFrequency = 0;
self.lastPeekTime = 0;
self.lastSoundTime = 0;
self.requiresMouthOpen = false;
self.requiresSound = false;
// Create character visual
var characterGraphics = self.attachAsset('hiddenCharacter', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4
});
// Create found effect (initially invisible)
var foundEffect = self.attachAsset('foundEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
// Setup character with specific behavior
self.setup = function (config) {
self.peekFrequency = config.peekFrequency || 0;
self.peekDuration = config.peekDuration || 0;
self.soundFrequency = config.soundFrequency || 0;
self.requiresMouthOpen = config.requiresMouthOpen || false;
self.requiresSound = config.requiresSound || false;
// Set initial state
if (self.peekFrequency === 0) {
characterGraphics.alpha = 0.3; // Always slightly visible
} else {
characterGraphics.alpha = 0; // Hide initially
}
};
// Check if the character is currently revealing itself
self.update = function () {
// Skip if already found
if (self.isFound) {
return;
}
// Handle peek behavior
if (self.peekFrequency > 0) {
var currentTime = Date.now();
// Time to start a new peek
if (currentTime - self.lastPeekTime > self.peekFrequency) {
self.lastPeekTime = currentTime;
characterGraphics.alpha = 0.5;
// Set timer to hide again
LK.setTimeout(function () {
if (!self.isFound) {
characterGraphics.alpha = 0;
}
}, self.peekDuration);
}
}
// Handle sound behavior
if (self.soundFrequency > 0) {
var currentTime = Date.now();
// Time to make a sound
if (currentTime - self.lastSoundTime > self.soundFrequency) {
self.lastSoundTime = currentTime;
LK.getSound('characterSound').play();
}
}
};
// Check if player can find this character
self.checkFindCondition = function () {
if (self.isFound) {
return false;
}
var canFind = true;
// Check for mouth open requirement
if (self.requiresMouthOpen && !facekit.mouthOpen) {
canFind = false;
}
// Check for sound requirement
if (self.requiresSound && facekit.volume < 0.3) {
canFind = false;
}
return canFind;
};
// Mark as found and show animation
self.findCharacter = function () {
if (self.isFound) {
return;
}
self.isFound = true;
characterGraphics.alpha = 1;
// Switch to found appearance
tween(characterGraphics, {
tint: 0x22aa22
}, {
duration: 500
});
// Show found effect
foundEffect.alpha = 0.7;
tween(foundEffect, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1000
});
// Play sound
LK.getSound('found').play();
return true;
};
return self;
});
var TimerBar = Container.expand(function () {
var self = Container.call(this);
// Create background bar
var barBg = self.attachAsset('timerBg', {
anchorX: 0,
anchorY: 0.5
});
// Create timer bar
var bar = self.attachAsset('timerBar', {
anchorX: 0,
anchorY: 0.5
});
// Properties
self.maxTime = 60; // seconds
self.currentTime = 60;
self.warningThreshold = 10; // seconds
self.hasWarnedLowTime = false;
// Initialize with time
self.setTime = function (time) {
self.maxTime = time;
self.currentTime = time;
self.updateVisual();
};
// Update time remaining
self.updateTime = function (delta) {
self.currentTime -= delta;
if (self.currentTime < 0) {
self.currentTime = 0;
}
// Check for low time warning
if (self.currentTime <= self.warningThreshold && !self.hasWarnedLowTime) {
self.hasWarnedLowTime = true;
LK.getSound('timeWarn').play();
tween(bar, {
tint: 0xff0000
}, {
duration: 500
});
}
self.updateVisual();
return self.currentTime <= 0;
};
// Update visual representation
self.updateVisual = function () {
var percentage = self.currentTime / self.maxTime;
bar.scale.x = percentage;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state variables
var level = 1;
var charactersFound = 0;
var totalCharacters = 0;
var gameActive = false;
var lastTime = Date.now();
var timeSinceLastTick = 0;
var characters = [];
var timerBar;
var levelTime = 60; // seconds
var isLevelComplete = false;
// UI Elements
var scoreTxt;
var levelTxt;
var findTxt;
var instructionTxt;
// Initialize game
function initGame() {
// Set up UI
setupUI();
// Start game with level 1
startLevel(level);
// Start background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Setup UI elements
function setupUI() {
// Create score text
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
// Create level text
levelTxt = new Text2('Level: 1', {
size: 80,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.y = 100;
// Create find text (shows how many characters left to find)
findTxt = new Text2('Find: 0/0', {
size: 80,
fill: 0xFFFFFF
});
findTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(findTxt);
// Create instruction text
instructionTxt = new Text2('Find the hidden characters!', {
size: 60,
fill: 0xFFFFFF
});
instructionTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionTxt);
// Create timer bar
timerBar = new TimerBar();
timerBar.x = 2048 / 2 - 900; // Center
timerBar.y = 120;
LK.gui.top.addChild(timerBar);
}
// Start a new level
function startLevel(levelNum) {
// Clean up previous level
cleanupLevel();
// Update variables
level = levelNum;
charactersFound = 0;
isLevelComplete = false;
// Set level time (decreases with each level)
levelTime = Math.max(30, 60 - (level - 1) * 5);
// Update UI
levelTxt.setText('Level: ' + level);
// Calculate number of characters for this level
totalCharacters = 3 + Math.min(7, Math.floor(level / 2));
// Update find text
findTxt.setText('Find: 0/' + totalCharacters);
// Show appropriate instructions
if (level === 1) {
instructionTxt.setText('Look around to find the hidden characters!');
} else if (level === 2) {
instructionTxt.setText('Some characters need you to open your mouth!');
} else if (level === 3) {
instructionTxt.setText('Try making sounds to find some characters!');
} else {
instructionTxt.setText('Use all your skills to find the hidden characters!');
}
// Create characters
createCharacters();
// Reset timer
timerBar.setTime(levelTime);
timerBar.hasWarnedLowTime = false;
// Start the game
gameActive = true;
lastTime = Date.now();
}
// Create characters for the current level
function createCharacters() {
// Clear any existing characters
characters = [];
for (var i = 0; i < totalCharacters; i++) {
var character = new Character();
// Position randomly on screen
character.x = 200 + Math.random() * (2048 - 400);
character.y = 200 + Math.random() * (2732 - 400);
// Determine character behavior based on level and position in array
var config = {
peekFrequency: 0,
peekDuration: 0,
soundFrequency: 0,
requiresMouthOpen: false,
requiresSound: false
};
// Different character behaviors based on index and level
if (i < totalCharacters / 3) {
// Basic characters - just peek occasionally
config.peekFrequency = 3000 + Math.random() * 5000;
config.peekDuration = 500 + Math.random() * 1000;
} else if (i < totalCharacters * 2 / 3) {
// Medium characters - require mouth to be open
if (level >= 2) {
config.requiresMouthOpen = true;
config.peekFrequency = 4000 + Math.random() * 6000;
config.peekDuration = 400 + Math.random() * 800;
} else {
config.peekFrequency = 3000 + Math.random() * 5000;
config.peekDuration = 500 + Math.random() * 1000;
}
} else {
// Advanced characters - require sound
if (level >= 3) {
config.requiresSound = true;
config.soundFrequency = 7000 + Math.random() * 10000;
} else {
config.peekFrequency = 4000 + Math.random() * 6000;
config.peekDuration = 300 + Math.random() * 600;
}
}
// Setup character
character.setup(config);
// Add to game and tracking array
game.addChild(character);
characters.push(character);
}
}
// Clean up the current level
function cleanupLevel() {
// Remove all characters
for (var i = 0; i < characters.length; i++) {
if (characters[i].parent) {
characters[i].parent.removeChild(characters[i]);
}
}
characters = [];
}
// Update game state
game.update = function () {
// Skip if game not active
if (!gameActive) {
return;
}
// Calculate time delta
var currentTime = Date.now();
var delta = (currentTime - lastTime) / 1000; // Convert to seconds
lastTime = currentTime;
// Update timer
var timeUp = timerBar.updateTime(delta);
if (timeUp && !isLevelComplete) {
gameOver();
return;
}
// Update each character
for (var i = 0; i < characters.length; i++) {
characters[i].update();
}
};
// Handle touch/click on game
game.down = function (x, y, obj) {
// Skip if game not active
if (!gameActive) {
return;
}
// Check each character to see if clicked
for (var i = 0; i < characters.length; i++) {
var character = characters[i];
// Check if touch is on this character
if (x >= character.x - 100 && x <= character.x + 100 && y >= character.y - 100 && y <= character.y + 100) {
// Check if this character can be found based on conditions
if (character.checkFindCondition()) {
// Find the character
if (character.findCharacter()) {
// Update found count
charactersFound++;
// Update UI
findTxt.setText('Find: ' + charactersFound + '/' + totalCharacters);
// Add score
var scoreGain = 100 + Math.floor(timerBar.currentTime * 10);
LK.setScore(LK.getScore() + scoreGain);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if level complete
if (charactersFound >= totalCharacters) {
levelComplete();
}
}
}
}
}
};
// Handle move events
game.move = function (x, y, obj) {
// Game uses camera, no specific move handling needed
};
// Level complete
function levelComplete() {
isLevelComplete = true;
gameActive = false;
// Show level complete message
instructionTxt.setText('Level ' + level + ' Complete! Starting next level...');
// Wait a moment before starting next level
LK.setTimeout(function () {
startLevel(level + 1);
}, 3000);
}
// Game over
function gameOver() {
gameActive = false;
// Show game over message
instructionTxt.setText('Time\'s up! Game Over!');
// Short delay before showing game over screen
LK.setTimeout(function () {
LK.showGameOver();
}, 2000);
}
// Initialize the game
initGame();