/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
//Storage library which should be used for persistent game data
// var storage = LK.import('@upit/storage.v1');
//Library for using the camera (the background becomes the user's camera video feed) and the microphone. It can access face coordinates for interactive play, as well detect microphone volume / voice interactions
// var facekit = LK.import('@upit/facekit.v1');
//Classes can only be defined here. You cannot create inline classes in the games code.
var Bullet = Container.expand(function () {
var self = Container.call(this);
//Create and attach asset. This is the same as calling var xxx = self.addChild(LK.getAsset(...))
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
//Set bullet speed
self.speed = 20; // Bullet speed
//If this instance of bullet is attached, this method will be called every tick by the LK engine automatically.
//To not manually call .updated methods. If you want to manually call a method every tick on a class, use a different name than update.
//As .update is called from the LK engine directly, .update cannot have method arguments.
self.update = function () {
self.y -= self.speed; // Move bullet upwards
};
return self; //You must return self if you want other classes to be able to inherit from this class
});
var Door = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('door_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
//Make a Character class by using the LK expand method to extend Container.
var HealthBar = Container.expand(function () {
var self = Container.call(this);
var maxWidth = 200;
var height = 20;
var background = self.addChild(LK.getAsset('bullet', {
// Using bullet as a temporary graphic
width: maxWidth,
height: height,
tint: 0x808080,
// Grey background
anchorX: 0,
anchorY: 0
}));
var foreground = self.addChild(LK.getAsset('bullet', {
// Using bullet as a temporary graphic
width: maxWidth,
height: height,
tint: 0x00ff00,
// Green foreground
anchorX: 0,
anchorY: 0
}));
self.setMaxHealth = function (maxHealth) {
self._maxHealth = maxHealth;
self.setHealth(maxHealth);
};
self.setHealth = function (health) {
self._health = Math.max(0, Math.min(self._maxHealth, health));
foreground.width = self._health / self._maxHealth * maxWidth;
};
self.getHealth = function () {
return self._health;
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('key_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Note = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('note_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var PlayerCharacter = Container.expand(function () {
var self = Container.call(this);
//Get and automatically addChild to self asset with id 'player' with the anchor point set to .5, .5
var characterGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Clamp position to be within the bottom half of the screen
// Ensure self.width and self.height are valid (assets are loaded)
if (self.width && self.height) {
self.x = Math.max(characterGraphics.width / 2, Math.min(2048 - characterGraphics.width / 2, self.x));
// Y position is not changed by horizontal swipes, but we keep its clamping logic.
// Y position can now be changed by vertical swipes. Clamp Y to full screen height.
self.y = Math.max(characterGraphics.height / 2, Math.min(2732 - characterGraphics.height / 2, self.y));
}
};
return self; //You must return self if you want other classes to be able to inherit from this class
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Golden key
// Light yellow for the note
// Add key asset
//Minimalistic tween library which should be used for animations over time, including tinting / colouring an object, scaling, rotating, or changing any game object property.
//Only include the plugins you need to create the game.
//We have access to the following plugins. (Note that the variable names used are mandetory for each plugin)
// Initialize music
// Blue character
// Purple alien
// Yellow bullet
// Initialize assets used in this game. Scale them according to what is needed for the game.
// or via static code analysis based on their usage in the code.
// Assets are automatically created and loaded either dynamically during gameplay
/*
Supported Types:
1. Shape:
- Simple geometric figures with these properties:
* width: (required) pixel width of the shape.
* height: (required) pixel height of the shape.
* color: (required) color of the shape.
* shape: (required) type of shape. Valid options: 'box', 'ellipse'.
2. Image:
- Imported images with these properties:
* width: (required) pixel resolution width.
* height: (required) pixel resolution height.
* id: (required) identifier for the image.
* flipX: (optional) horizontal flip. Valid values: 0 (no flip), 1 (flip).
* flipY: (optional) vertical flip. Valid values: 0 (no flip), 1 (flip).
* orientation: (optional) rotation in multiples of 90 degrees, clockwise. Valid values:
- 0: No rotation.
- 1: Rotate 90 degrees.
- 2: Rotate 180 degrees.
- 3: Rotate 270 degrees.
Note: Width and height remain unchanged upon flipping.
3. Sound:
- Sound effects with these properties:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
4. Music:
- In contract to sound effects, only one music can be played at a time
- Music is using the same API to initilize just like sound.
- Music loops by default
- Music with these config options:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
* start: (optional) a float from 0 to 1 used for cropping and indicates the start of the cropping
* end: (optional) a float from 0 to 1 used for cropping and indicates the end of the cropping
*/
//Init game with black background
//Note game dimensions are 2048x2732
// Global variables
var alien;
var character;
var bullets = [];
var scoreTxt;
// dragNode is no longer used with swipe controls
var lastCharacterIntersectingAlien = false;
var touchStartX = 0; // Stores the initial X position of a touch/swipe
var touchStartY = 0; // Stores the initial Y position of a touch/swipe
var characterInitialX = 0; // Stores the character's X position at the start of a swipe
var characterInitialY = 0; // Stores the character's Y position at the start of a swipe
var isSwiping = false; // Flag to indicate if a swipe is in progress
var swipeAxis = 'none'; // Determines swipe axis: 'none', 'horizontal', 'vertical'
var SWIPE_THRESHOLD = 20; // Minimum pixel distance to determine swipe axis
var WIN_SCORE = 50; // Score to win
// var monsterHealth = 100; // Monster's initial health (REMOVED)
// var monsterMaxHealth = 100; // Monster's maximum health (REMOVED)
// var healthBar; // Health bar instance (REMOVED)
var alienHitCount = 0; // Tracks how many times the alien has been hit
var alienDefeated = false; // Tracks if the alien has been defeated and dropped the item
var keyInstance = null; // Key instance
var hasKey = false; // Tracks if the player has collected the key
var noteInstance = null; // Note instance
var doorInstance = null; // Door instance
var playerLives = 3; // Player starts with 3 lives
var characterLastIntersectingDoor = false; // Tracks if character was intersecting door in the previous frame
// var monsterLives = 4; // Monster starts with 4 lives (REMOVED)
// Change background color
game.setBackgroundColor(0x1a1a1a); // Dark grey background
// Create and position the alien in the center
alien = game.addChild(LK.getAsset('alien', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
}));
alien.lastIntersectingCharacter = false; // Initialize for intersection tracking
alien.lastIntersectingBullet = false; // Initialize for intersection tracking
// Health bar creation removed as per new hit count logic
// Create and position the player character at the bottom center
character = game.addChild(new PlayerCharacter());
character.x = 2048 / 2;
character.y = 2732 - character.height / 2 - 100; // Position near bottom
character.lastIntersectingAlien = false; // Initialize for intersection tracking
// Create and position the door
doorInstance = game.addChild(new Door());
doorInstance.x = 2048 / 2;
doorInstance.y = 150; // Position at the top-center area
// Initialize key variables
keyInstance = null;
hasKey = false;
// Create and add score display
scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.setText(LK.getScore());
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Mouse or touch up event on the game object
// Mouse or touch down event on the game object to start dragging the character
game.down = function (x, y, obj) {
// Initialize swipe tracking variables
touchStartX = x;
touchStartY = y;
if (character) {
// Ensure character exists
characterInitialX = character.x;
characterInitialY = character.y;
}
isSwiping = true;
swipeAxis = 'none'; // Reset swipe axis determination
// Check if the key was tapped
if (keyInstance !== null && !hasKey) {
// Only allow tap if key exists and not yet collected
var keyLocalPos = keyInstance.toLocal({
x: x,
y: y
});
// Assuming keyInstance has its asset as the first child and anchor is 0.5, 0.5
var keyAsset = keyInstance.children[0];
if (keyAsset && Math.abs(keyLocalPos.x) <= keyAsset.width / 2 && Math.abs(keyLocalPos.y) <= keyAsset.height / 2) {
hasKey = true; // Player now "has" the key
// keyInstance is NOT destroyed. It will now follow the player.
LK.getSound('Rah').play(); // Play sound for collecting the key (re-using 'Rah')
// The game doesn't end here; player now has the key for a future door.
isSwiping = false; // Stop swipe processing as an action occurred
swipeAxis = 'none';
return; // Exit early, an action was performed
}
}
};
game.up = function (x, y, obj) {
// Stop character movement on touch up
isSwiping = false;
swipeAxis = 'none'; // Reset swipe axis
};
// Ask LK engine to update game every game tick
// Mouse or touch move event on the game object to move the character
game.move = function (x, y, obj) {
if (isSwiping && character) {
var currentX = x; // Current touch x from event
var currentY = y; // Current touch y from event
var deltaX = currentX - touchStartX;
var deltaY = currentY - touchStartY;
if (swipeAxis === 'none') {
// Determine swipe axis if not already determined
if (Math.abs(deltaX) > SWIPE_THRESHOLD || Math.abs(deltaY) > SWIPE_THRESHOLD) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
swipeAxis = 'horizontal';
} else {
swipeAxis = 'vertical';
}
}
}
// Move character based on determined swipe axis
if (swipeAxis === 'horizontal') {
character.x = characterInitialX + deltaX;
} else if (swipeAxis === 'vertical') {
character.y = characterInitialY + deltaY;
}
// Clamping will be handled by character.update()
}
};
game.update = function () {
// Update and manage bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// We always keep track of last and current values to detect state changes
if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Initialize lastY for tracking changes on Y
if (bullet.lastIntersectingAlien === undefined) bullet.lastIntersectingAlien = false; // Initialize for intersection tracking
// Off-Screen Detection: Destroy bullet if it goes off the top edge
if (bullet.lastY >= -bullet.height && bullet.y < -bullet.height) {
bullet.destroy(); // Destroy can only be called from the main 'Game' class
bullets.splice(i, 1);
continue; // Skip further checks for this bullet
}
// Intersection Detection: Check for bullet collision with alien
var currentIntersectingAlien = bullet.intersects(alien);
if (!bullet.lastIntersectingAlien && currentIntersectingAlien) {
// Intersection just started
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('hit').play(); // Play hit sound
// Increment alien hit count
alienHitCount++;
// Check if alien has been hit 4 times and not yet defeated
if (alienHitCount >= 4 && !alienDefeated) {
alienDefeated = true; // Mark alien as defeated to prevent multiple item drops
if (keyInstance === null) {
// Check if keyInstance is null
// Create key only if it doesn't exist
keyInstance = game.addChild(new Key()); // Create a Key instance
keyInstance.x = alien.x; // Position key at alien's last position
keyInstance.y = alien.y;
// Drop a note as well
if (noteInstance === null) {
// Check if noteInstance is null
noteInstance = game.addChild(new Note());
noteInstance.x = alien.x + (keyInstance.width || 80) + 20; // Position note next to the key
noteInstance.y = alien.y;
}
// The alien is now "defeated" in terms of dropping the key.
// Alien continues to be active unless explicitly made inactive later.
}
}
// Winning condition (if score is still a win condition)
if (LK.getScore() >= WIN_SCORE) {
LK.showYouWin(); // Show "you win" and reset
}
bullet.destroy(); // Destroy bullet on hit
bullets.splice(i, 1);
continue; // Skip further checks for this bullet
}
// Update last known states
bullet.lastY = bullet.y;
bullet.lastIntersectingAlien = currentIntersectingAlien;
}
// Character movement is handled by the PlayerCharacter update method.
// Move the alien towards the character (only if not fully defeated, e.g. invisible/inactive)
// For simplicity, alien continues to move. If you want it to stop after dropping note:
// if (!alienDefeated || (alienDefeated && noteInstance !== null)) { // Example: stop if note is dropped and still on screen
if (alien && alien.visible) {
// Assuming alien might be made invisible
var moveSpeed = 5; // Adjust speed as needed
if (character && character.visible) {
// Ensure character exists and is visible
var dx = character.x - alien.x;
var dy = character.y - alien.y;
var angle = Math.atan2(dy, dx);
alien.x += Math.cos(angle) * moveSpeed;
alien.y += Math.sin(angle) * moveSpeed;
}
}
// Check for character collision with alien
if (alien && alien.visible && character && character.visible) {
// Check visibility
var currentCharacterIntersectingAlien = character.intersects(alien);
if (!lastCharacterIntersectingAlien && currentCharacterIntersectingAlien) {
// Collision just started
playerLives--; // Player loses a life
LK.effects.flashScreen(0xff0000, 1000); // Flash screen red
if (playerLives <= 0) {
LK.showGameOver(); // Show game over and reset
} else {
// Player still has lives.
}
}
lastCharacterIntersectingAlien = currentCharacterIntersectingAlien;
} else {
lastCharacterIntersectingAlien = false; // Reset if alien or character is not active for collision
}
// Make the key follow the player if collected
if (hasKey && keyInstance && character && character.visible) {
// Position key slightly above the player character
var keyAssetGraphics = keyInstance.children[0]; // Assuming asset is the first child
var keyHeight = keyAssetGraphics ? keyAssetGraphics.height : 80; // Use actual or default height
var playerCharGraphics = character.children[0];
var playerHeight = playerCharGraphics ? playerCharGraphics.height : 100;
keyInstance.x = character.x;
keyInstance.y = character.y - playerHeight / 2 - keyHeight / 2 - 10; // Adjust offset as needed
}
// Check for player collision with door if player has the key
var currentCharacterIntersectingDoor = false; // Local var for current frame's intersection state with the door
if (doorInstance && doorInstance.visible && character && character.visible && hasKey) {
// Ensure door, character are valid and visible, and player has the key
currentCharacterIntersectingDoor = character.intersects(doorInstance);
if (!characterLastIntersectingDoor && currentCharacterIntersectingDoor) {
// Check for the start of an intersection
// Intersection just started
doorInstance.visible = false; // "Opens" the door by making it invisible
if (keyInstance && keyInstance.visible) {
// If key exists and is visible
keyInstance.visible = false; // Make the key invisible as it has been "used"
}
LK.showYouWin(); // Player with key + door collision = win (transition to "new room")
}
}
// Update the 'last intersecting' state for the door for the next frame.
// This is crucial for detecting only the *initial* moment of intersection.
if (doorInstance && character) {
// Only update if door and character are still valid
characterLastIntersectingDoor = currentCharacterIntersectingDoor;
} else {
// If door or character becomes invalid (e.g., destroyed), reset the tracking state.
characterLastIntersectingDoor = false;
}
// Fire a bullet periodically (every 15 ticks = ~4 times per second)
if (LK.ticks % 15 == 0) {
var newBullet = new Bullet();
// We always keep track of last and current values
newBullet.x = character.x;
newBullet.y = character.y;
newBullet.lastY = newBullet.y; // Initialize lastY for tracking changes on Y
newBullet.lastIntersectingAlien = newBullet.intersects(alien); // Initialize for intersection tracking
bullets.push(newBullet);
game.addChild(newBullet);
LK.getSound('shoot').play(); // Play shoot sound
}
};
// Play background music
LK.playMusic('bgmusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
//Storage library which should be used for persistent game data
// var storage = LK.import('@upit/storage.v1');
//Library for using the camera (the background becomes the user's camera video feed) and the microphone. It can access face coordinates for interactive play, as well detect microphone volume / voice interactions
// var facekit = LK.import('@upit/facekit.v1');
//Classes can only be defined here. You cannot create inline classes in the games code.
var Bullet = Container.expand(function () {
var self = Container.call(this);
//Create and attach asset. This is the same as calling var xxx = self.addChild(LK.getAsset(...))
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
//Set bullet speed
self.speed = 20; // Bullet speed
//If this instance of bullet is attached, this method will be called every tick by the LK engine automatically.
//To not manually call .updated methods. If you want to manually call a method every tick on a class, use a different name than update.
//As .update is called from the LK engine directly, .update cannot have method arguments.
self.update = function () {
self.y -= self.speed; // Move bullet upwards
};
return self; //You must return self if you want other classes to be able to inherit from this class
});
var Door = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('door_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
//Make a Character class by using the LK expand method to extend Container.
var HealthBar = Container.expand(function () {
var self = Container.call(this);
var maxWidth = 200;
var height = 20;
var background = self.addChild(LK.getAsset('bullet', {
// Using bullet as a temporary graphic
width: maxWidth,
height: height,
tint: 0x808080,
// Grey background
anchorX: 0,
anchorY: 0
}));
var foreground = self.addChild(LK.getAsset('bullet', {
// Using bullet as a temporary graphic
width: maxWidth,
height: height,
tint: 0x00ff00,
// Green foreground
anchorX: 0,
anchorY: 0
}));
self.setMaxHealth = function (maxHealth) {
self._maxHealth = maxHealth;
self.setHealth(maxHealth);
};
self.setHealth = function (health) {
self._health = Math.max(0, Math.min(self._maxHealth, health));
foreground.width = self._health / self._maxHealth * maxWidth;
};
self.getHealth = function () {
return self._health;
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('key_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Note = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('note_asset', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var PlayerCharacter = Container.expand(function () {
var self = Container.call(this);
//Get and automatically addChild to self asset with id 'player' with the anchor point set to .5, .5
var characterGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Clamp position to be within the bottom half of the screen
// Ensure self.width and self.height are valid (assets are loaded)
if (self.width && self.height) {
self.x = Math.max(characterGraphics.width / 2, Math.min(2048 - characterGraphics.width / 2, self.x));
// Y position is not changed by horizontal swipes, but we keep its clamping logic.
// Y position can now be changed by vertical swipes. Clamp Y to full screen height.
self.y = Math.max(characterGraphics.height / 2, Math.min(2732 - characterGraphics.height / 2, self.y));
}
};
return self; //You must return self if you want other classes to be able to inherit from this class
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Golden key
// Light yellow for the note
// Add key asset
//Minimalistic tween library which should be used for animations over time, including tinting / colouring an object, scaling, rotating, or changing any game object property.
//Only include the plugins you need to create the game.
//We have access to the following plugins. (Note that the variable names used are mandetory for each plugin)
// Initialize music
// Blue character
// Purple alien
// Yellow bullet
// Initialize assets used in this game. Scale them according to what is needed for the game.
// or via static code analysis based on their usage in the code.
// Assets are automatically created and loaded either dynamically during gameplay
/*
Supported Types:
1. Shape:
- Simple geometric figures with these properties:
* width: (required) pixel width of the shape.
* height: (required) pixel height of the shape.
* color: (required) color of the shape.
* shape: (required) type of shape. Valid options: 'box', 'ellipse'.
2. Image:
- Imported images with these properties:
* width: (required) pixel resolution width.
* height: (required) pixel resolution height.
* id: (required) identifier for the image.
* flipX: (optional) horizontal flip. Valid values: 0 (no flip), 1 (flip).
* flipY: (optional) vertical flip. Valid values: 0 (no flip), 1 (flip).
* orientation: (optional) rotation in multiples of 90 degrees, clockwise. Valid values:
- 0: No rotation.
- 1: Rotate 90 degrees.
- 2: Rotate 180 degrees.
- 3: Rotate 270 degrees.
Note: Width and height remain unchanged upon flipping.
3. Sound:
- Sound effects with these properties:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
4. Music:
- In contract to sound effects, only one music can be played at a time
- Music is using the same API to initilize just like sound.
- Music loops by default
- Music with these config options:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
* start: (optional) a float from 0 to 1 used for cropping and indicates the start of the cropping
* end: (optional) a float from 0 to 1 used for cropping and indicates the end of the cropping
*/
//Init game with black background
//Note game dimensions are 2048x2732
// Global variables
var alien;
var character;
var bullets = [];
var scoreTxt;
// dragNode is no longer used with swipe controls
var lastCharacterIntersectingAlien = false;
var touchStartX = 0; // Stores the initial X position of a touch/swipe
var touchStartY = 0; // Stores the initial Y position of a touch/swipe
var characterInitialX = 0; // Stores the character's X position at the start of a swipe
var characterInitialY = 0; // Stores the character's Y position at the start of a swipe
var isSwiping = false; // Flag to indicate if a swipe is in progress
var swipeAxis = 'none'; // Determines swipe axis: 'none', 'horizontal', 'vertical'
var SWIPE_THRESHOLD = 20; // Minimum pixel distance to determine swipe axis
var WIN_SCORE = 50; // Score to win
// var monsterHealth = 100; // Monster's initial health (REMOVED)
// var monsterMaxHealth = 100; // Monster's maximum health (REMOVED)
// var healthBar; // Health bar instance (REMOVED)
var alienHitCount = 0; // Tracks how many times the alien has been hit
var alienDefeated = false; // Tracks if the alien has been defeated and dropped the item
var keyInstance = null; // Key instance
var hasKey = false; // Tracks if the player has collected the key
var noteInstance = null; // Note instance
var doorInstance = null; // Door instance
var playerLives = 3; // Player starts with 3 lives
var characterLastIntersectingDoor = false; // Tracks if character was intersecting door in the previous frame
// var monsterLives = 4; // Monster starts with 4 lives (REMOVED)
// Change background color
game.setBackgroundColor(0x1a1a1a); // Dark grey background
// Create and position the alien in the center
alien = game.addChild(LK.getAsset('alien', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
}));
alien.lastIntersectingCharacter = false; // Initialize for intersection tracking
alien.lastIntersectingBullet = false; // Initialize for intersection tracking
// Health bar creation removed as per new hit count logic
// Create and position the player character at the bottom center
character = game.addChild(new PlayerCharacter());
character.x = 2048 / 2;
character.y = 2732 - character.height / 2 - 100; // Position near bottom
character.lastIntersectingAlien = false; // Initialize for intersection tracking
// Create and position the door
doorInstance = game.addChild(new Door());
doorInstance.x = 2048 / 2;
doorInstance.y = 150; // Position at the top-center area
// Initialize key variables
keyInstance = null;
hasKey = false;
// Create and add score display
scoreTxt = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
scoreTxt.setText(LK.getScore());
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Mouse or touch up event on the game object
// Mouse or touch down event on the game object to start dragging the character
game.down = function (x, y, obj) {
// Initialize swipe tracking variables
touchStartX = x;
touchStartY = y;
if (character) {
// Ensure character exists
characterInitialX = character.x;
characterInitialY = character.y;
}
isSwiping = true;
swipeAxis = 'none'; // Reset swipe axis determination
// Check if the key was tapped
if (keyInstance !== null && !hasKey) {
// Only allow tap if key exists and not yet collected
var keyLocalPos = keyInstance.toLocal({
x: x,
y: y
});
// Assuming keyInstance has its asset as the first child and anchor is 0.5, 0.5
var keyAsset = keyInstance.children[0];
if (keyAsset && Math.abs(keyLocalPos.x) <= keyAsset.width / 2 && Math.abs(keyLocalPos.y) <= keyAsset.height / 2) {
hasKey = true; // Player now "has" the key
// keyInstance is NOT destroyed. It will now follow the player.
LK.getSound('Rah').play(); // Play sound for collecting the key (re-using 'Rah')
// The game doesn't end here; player now has the key for a future door.
isSwiping = false; // Stop swipe processing as an action occurred
swipeAxis = 'none';
return; // Exit early, an action was performed
}
}
};
game.up = function (x, y, obj) {
// Stop character movement on touch up
isSwiping = false;
swipeAxis = 'none'; // Reset swipe axis
};
// Ask LK engine to update game every game tick
// Mouse or touch move event on the game object to move the character
game.move = function (x, y, obj) {
if (isSwiping && character) {
var currentX = x; // Current touch x from event
var currentY = y; // Current touch y from event
var deltaX = currentX - touchStartX;
var deltaY = currentY - touchStartY;
if (swipeAxis === 'none') {
// Determine swipe axis if not already determined
if (Math.abs(deltaX) > SWIPE_THRESHOLD || Math.abs(deltaY) > SWIPE_THRESHOLD) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
swipeAxis = 'horizontal';
} else {
swipeAxis = 'vertical';
}
}
}
// Move character based on determined swipe axis
if (swipeAxis === 'horizontal') {
character.x = characterInitialX + deltaX;
} else if (swipeAxis === 'vertical') {
character.y = characterInitialY + deltaY;
}
// Clamping will be handled by character.update()
}
};
game.update = function () {
// Update and manage bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// We always keep track of last and current values to detect state changes
if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Initialize lastY for tracking changes on Y
if (bullet.lastIntersectingAlien === undefined) bullet.lastIntersectingAlien = false; // Initialize for intersection tracking
// Off-Screen Detection: Destroy bullet if it goes off the top edge
if (bullet.lastY >= -bullet.height && bullet.y < -bullet.height) {
bullet.destroy(); // Destroy can only be called from the main 'Game' class
bullets.splice(i, 1);
continue; // Skip further checks for this bullet
}
// Intersection Detection: Check for bullet collision with alien
var currentIntersectingAlien = bullet.intersects(alien);
if (!bullet.lastIntersectingAlien && currentIntersectingAlien) {
// Intersection just started
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('hit').play(); // Play hit sound
// Increment alien hit count
alienHitCount++;
// Check if alien has been hit 4 times and not yet defeated
if (alienHitCount >= 4 && !alienDefeated) {
alienDefeated = true; // Mark alien as defeated to prevent multiple item drops
if (keyInstance === null) {
// Check if keyInstance is null
// Create key only if it doesn't exist
keyInstance = game.addChild(new Key()); // Create a Key instance
keyInstance.x = alien.x; // Position key at alien's last position
keyInstance.y = alien.y;
// Drop a note as well
if (noteInstance === null) {
// Check if noteInstance is null
noteInstance = game.addChild(new Note());
noteInstance.x = alien.x + (keyInstance.width || 80) + 20; // Position note next to the key
noteInstance.y = alien.y;
}
// The alien is now "defeated" in terms of dropping the key.
// Alien continues to be active unless explicitly made inactive later.
}
}
// Winning condition (if score is still a win condition)
if (LK.getScore() >= WIN_SCORE) {
LK.showYouWin(); // Show "you win" and reset
}
bullet.destroy(); // Destroy bullet on hit
bullets.splice(i, 1);
continue; // Skip further checks for this bullet
}
// Update last known states
bullet.lastY = bullet.y;
bullet.lastIntersectingAlien = currentIntersectingAlien;
}
// Character movement is handled by the PlayerCharacter update method.
// Move the alien towards the character (only if not fully defeated, e.g. invisible/inactive)
// For simplicity, alien continues to move. If you want it to stop after dropping note:
// if (!alienDefeated || (alienDefeated && noteInstance !== null)) { // Example: stop if note is dropped and still on screen
if (alien && alien.visible) {
// Assuming alien might be made invisible
var moveSpeed = 5; // Adjust speed as needed
if (character && character.visible) {
// Ensure character exists and is visible
var dx = character.x - alien.x;
var dy = character.y - alien.y;
var angle = Math.atan2(dy, dx);
alien.x += Math.cos(angle) * moveSpeed;
alien.y += Math.sin(angle) * moveSpeed;
}
}
// Check for character collision with alien
if (alien && alien.visible && character && character.visible) {
// Check visibility
var currentCharacterIntersectingAlien = character.intersects(alien);
if (!lastCharacterIntersectingAlien && currentCharacterIntersectingAlien) {
// Collision just started
playerLives--; // Player loses a life
LK.effects.flashScreen(0xff0000, 1000); // Flash screen red
if (playerLives <= 0) {
LK.showGameOver(); // Show game over and reset
} else {
// Player still has lives.
}
}
lastCharacterIntersectingAlien = currentCharacterIntersectingAlien;
} else {
lastCharacterIntersectingAlien = false; // Reset if alien or character is not active for collision
}
// Make the key follow the player if collected
if (hasKey && keyInstance && character && character.visible) {
// Position key slightly above the player character
var keyAssetGraphics = keyInstance.children[0]; // Assuming asset is the first child
var keyHeight = keyAssetGraphics ? keyAssetGraphics.height : 80; // Use actual or default height
var playerCharGraphics = character.children[0];
var playerHeight = playerCharGraphics ? playerCharGraphics.height : 100;
keyInstance.x = character.x;
keyInstance.y = character.y - playerHeight / 2 - keyHeight / 2 - 10; // Adjust offset as needed
}
// Check for player collision with door if player has the key
var currentCharacterIntersectingDoor = false; // Local var for current frame's intersection state with the door
if (doorInstance && doorInstance.visible && character && character.visible && hasKey) {
// Ensure door, character are valid and visible, and player has the key
currentCharacterIntersectingDoor = character.intersects(doorInstance);
if (!characterLastIntersectingDoor && currentCharacterIntersectingDoor) {
// Check for the start of an intersection
// Intersection just started
doorInstance.visible = false; // "Opens" the door by making it invisible
if (keyInstance && keyInstance.visible) {
// If key exists and is visible
keyInstance.visible = false; // Make the key invisible as it has been "used"
}
LK.showYouWin(); // Player with key + door collision = win (transition to "new room")
}
}
// Update the 'last intersecting' state for the door for the next frame.
// This is crucial for detecting only the *initial* moment of intersection.
if (doorInstance && character) {
// Only update if door and character are still valid
characterLastIntersectingDoor = currentCharacterIntersectingDoor;
} else {
// If door or character becomes invalid (e.g., destroyed), reset the tracking state.
characterLastIntersectingDoor = false;
}
// Fire a bullet periodically (every 15 ticks = ~4 times per second)
if (LK.ticks % 15 == 0) {
var newBullet = new Bullet();
// We always keep track of last and current values
newBullet.x = character.x;
newBullet.y = character.y;
newBullet.lastY = newBullet.y; // Initialize lastY for tracking changes on Y
newBullet.lastIntersectingAlien = newBullet.intersects(alien); // Initialize for intersection tracking
bullets.push(newBullet);
game.addChild(newBullet);
LK.getSound('shoot').play(); // Play shoot sound
}
};
// Play background music
LK.playMusic('bgmusic');
A blue demon alien. In-Game asset. High contrast. No shadows
The character is a little boy that is sad and 17 years old. In-Game asset. 2d. High contrast. No shadows. Kid
Key. In-Game asset. High contrast. No shadows. 2d
Door. In-Game asset. No shadows. 2d
A note that says dear player I ate classmates from alien. In-Game asset. High contrast. No shadows