/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
coinGraphics.rotation += 0.1;
};
return self;
});
var CrewMember = Container.expand(function () {
var self = Container.call(this);
var crewGraphics = self.attachAsset('crew', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
var pirateGraphics = self.attachAsset('pirate', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
pirateGraphics.rotation += 0.05;
};
return self;
});
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 3;
self.currentSpeed = self.baseSpeed;
self.isShielded = false;
self.shieldTime = 0;
self.stickyTime = 0;
self.isSizeModified = false;
self.sizeTime = 0;
self.originalScale = 1;
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
shieldGraphics.visible = false;
self.activateShield = function () {
self.isShielded = true;
self.shieldTime = 300;
shieldGraphics.visible = true;
};
self.activateStickyControls = function () {
self.stickyTime = 300;
};
self.setSizeModifier = function (scale, duration) {
self.isSizeModified = true;
self.sizeTime = duration;
tween(shipGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 200
});
tween(shieldGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 200
});
};
self.update = function () {
if (self.shieldTime > 0) {
self.shieldTime--;
if (self.shieldTime <= 0) {
self.isShielded = false;
shieldGraphics.visible = false;
}
}
if (self.stickyTime > 0) {
self.stickyTime--;
}
if (self.sizeTime > 0) {
self.sizeTime--;
if (self.sizeTime <= 0) {
self.isSizeModified = false;
tween(shipGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
tween(shieldGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
}
// Ship stays in place - world moves around it
};
return self;
});
var Stone = Container.expand(function () {
var self = Container.call(this);
var stoneGraphics = self.attachAsset('stone', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.update = function () {
self.y += self.speed;
stoneGraphics.rotation += 0.08;
};
return self;
});
var Wave = Container.expand(function () {
var self = Container.call(this);
var waveGraphics = self.attachAsset('wave', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
waveGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var ship;
var obstacles = [];
var crewMembers = [];
var coins = [];
var hearts = [];
var level = 1;
var health = 3;
var crewCount = 0;
var coinCount = 0;
var distance = 0;
var distanceText = new Text2('Distance: 0m', {
size: 50,
fill: 0xFFFFFF
});
distanceText.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceText);
distanceText.x = 512;
var gameSpeed = 2;
var quizActive = false;
var quizTimer = 0;
var nextQuizTime = 300; // Start first quiz after 5 seconds
var doubleCoinsTime = 0;
var extraObstaclesTime = 0;
var clearPathTime = 0;
var scoreFreezeTime = 0;
// Quiz system
var quizQuestions = [{
question: "What does FOB stand for in shipping terms?",
answers: ["Free On Board", "Freight on Boat", "Foreign Object Bureau", "Full Ocean Bill"],
correct: 0
}, {
question: "Which maritime organization sets international shipping standards?",
answers: ["WHO", "IMO", "WTO", "ILO"],
correct: 1
}, {
question: "What is the front of a ship called?",
answers: ["Stern", "Port", "Bow", "Starboard"],
correct: 2
}, {
question: "EXW means the seller delivers when goods are:",
answers: ["At seller's premises", "On the ship", "At destination", "In transit"],
correct: 0
}, {
question: "What does CIF include in shipping terms?",
answers: ["Cost only", "Insurance only", "Freight only", "Cost, Insurance, Freight"],
correct: 3
}, {
question: "What is the right side of a ship called?",
answers: ["Port", "Starboard", "Bow", "Stern"],
correct: 1
}, {
question: "DDP means the seller is responsible for:",
answers: ["Delivery at port", "Delivery duty paid", "Dock departure point", "Direct delivery purchase"],
correct: 1
}, {
question: "What does TEU stand for in container shipping?",
answers: ["Total Export Unit", "Twenty-foot Equivalent Unit", "Terminal Exchange Unit", "Transport Equipment Unit"],
correct: 1
}, {
question: "Which term means 'Carrier alongside ship'?",
answers: ["CAS", "FAS", "DAS", "PAS"],
correct: 1
}, {
question: "What is the kitchen area on a ship called?",
answers: ["Mess", "Galley", "Cabin", "Hold"],
correct: 1
}, {
question: "CFR includes which costs?",
answers: ["Cost and freight", "Cost, freight, risk", "Customs, freight, rate", "Container freight rate"],
correct: 0
}, {
question: "What does DAP stand for?",
answers: ["Delivery at place", "Dock arrival point", "Delivered at place", "Direct arrival point"],
correct: 2
}, {
question: "The left side of a ship is called:",
answers: ["Starboard", "Port", "Bow", "Stern"],
correct: 1
}, {
question: "What is a ship's maximum cargo capacity called?",
answers: ["Deadweight", "Gross tonnage", "Net tonnage", "Displacement"],
correct: 0
}, {
question: "FCA means:",
answers: ["Free carrier", "Freight cost allowance", "Full container access", "Forward cargo arrangement"],
correct: 0
}];
var currentQuiz = null;
var quizContainer = null;
var quizTimeRemaining = 600; // 10 seconds
var effectDisplay = null;
var lastAppliedEffect = '';
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
LK.gui.topRight.addChild(levelText);
var crewText = new Text2('Crew: 0', {
size: 50,
fill: 0xFFFFFF
});
crewText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(crewText);
var coinText = new Text2('Coins: 0', {
size: 50,
fill: 0xffd700
});
coinText.anchor.set(1, 1);
LK.gui.bottomRight.addChild(coinText);
// Initialize game elements
ship = game.addChild(new Ship());
ship.x = 1024;
ship.y = 2400;
// Create hearts display
for (var i = 0; i < 3; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: 150 + i * 60,
y: 100
});
hearts.push(heart);
LK.gui.topLeft.addChild(heart);
}
function updateScore() {
if (scoreFreezeTime <= 0) {
var score = Math.floor(distance) + crewCount * 100;
LK.setScore(score);
scoreText.setText('Score: ' + score);
}
}
function updateLevel() {
var newLevel = Math.floor(distance / 800) + 1; // Level up faster for increased difficulty
if (newLevel > level && newLevel <= 4) {
level = newLevel;
levelText.setText('Level: ' + level);
gameSpeed += 0.5; // Increased speed boost per level
// Change background color based on level
if (level === 2) {
game.setBackgroundColor(0xFF8C00); // Orange sunset
} else if (level === 3) {
game.setBackgroundColor(0x191970); // Dark blue night
} else if (level === 4) {
game.setBackgroundColor(0x2F4F4F); // Dark gray storm
}
}
}
function spawnObstacle() {
if (clearPathTime > 0) return;
var obstacleType = Math.floor(Math.random() * 4);
var obstacle;
switch (obstacleType) {
case 0:
obstacle = new Obstacle();
break;
case 1:
obstacle = new Pirate();
break;
case 2:
obstacle = new Wave();
break;
case 3:
obstacle = new Stone();
break;
}
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = -100;
obstacle.speed = obstacle.speed + gameSpeed + level * 0.8; // Faster obstacles at higher levels
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCrewMember() {
var crew = new CrewMember();
crew.x = Math.random() * 1800 + 124;
crew.y = -100;
crew.speed = 3 + gameSpeed;
crewMembers.push(crew);
game.addChild(crew);
}
function spawnCoin() {
var coin = new Coin();
coin.x = Math.random() * 1800 + 124;
coin.y = -100;
coin.speed = 3 + gameSpeed;
coins.push(coin);
game.addChild(coin);
}
function showQuiz() {
if (quizActive) return;
quizActive = true;
quizTimeRemaining = 600;
currentQuiz = quizQuestions[Math.floor(Math.random() * quizQuestions.length)];
quizContainer = game.addChild(new Container());
var bg = quizContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.95
});
// Show current active effects
var effectsText = getActiveEffectsText();
if (effectsText) {
var effectDisplay = new Text2(effectsText, {
size: 45,
fill: 0xFFFF00
});
effectDisplay.anchor.set(0.5, 0.5);
effectDisplay.x = 1024;
effectDisplay.y = 900;
quizContainer.addChild(effectDisplay);
}
var questionText = new Text2(currentQuiz.question, {
size: 70,
fill: 0xFFFFFF
});
questionText.anchor.set(0.5, 0.5);
questionText.x = 1024;
questionText.y = 1100;
quizContainer.addChild(questionText);
var timerText = new Text2('Time: 10', {
size: 60,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 1000;
quizContainer.addChild(timerText);
quizContainer.timerText = timerText;
// Create answer buttons
quizContainer.answerButtons = [];
for (var i = 0; i < 4; i++) {
var answerContainer = new Container();
answerContainer.x = 1024;
answerContainer.y = 1300 + i * 120;
answerContainer.answerIndex = i;
var answerBg = answerContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.2,
alpha: 0.7
});
var answerText = new Text2(currentQuiz.answers[i], {
size: 50,
fill: 0xFFFFFF
});
answerText.anchor.set(0.5, 0.5);
answerContainer.addChild(answerText);
// Store index in closure to avoid reference issues
answerContainer.down = function (index) {
return function (x, y, obj) {
handleQuizAnswer(index);
};
}(i);
quizContainer.answerButtons.push(answerContainer);
quizContainer.addChild(answerContainer);
}
}
function handleQuizAnswer(answerIndex) {
if (!quizActive) return;
// Prevent multiple answers
quizActive = false;
var isCorrect = answerIndex === currentQuiz.correct;
if (isCorrect) {
LK.getSound('correct').play();
applyPowerUp();
} else {
LK.getSound('wrong').play();
applyPenalty();
}
// Show effect feedback for 2 seconds
if (lastAppliedEffect) {
showEffectFeedback(lastAppliedEffect, isCorrect);
}
hideQuiz();
nextQuizTime = LK.ticks + 300; // Every 5 seconds
}
function getActiveEffectsText() {
var effects = [];
if (ship.isShielded) effects.push('🛡️ Shield Active');
if (doubleCoinsTime > 0) effects.push('💰 Double Coins');
if (ship.isSizeModified && ship.children[0].scaleX < 1) effects.push('⬇️ Smaller Ship');
if (ship.isSizeModified && ship.children[0].scaleX > 1) effects.push('⬆️ Bigger Ship');
if (ship.stickyTime > 0) effects.push('🐌 Sticky Controls');
if (extraObstaclesTime > 0) effects.push('⚠️ Extra Obstacles');
if (scoreFreezeTime > 0) effects.push('❄️ Score Frozen');
if (clearPathTime > 0) effects.push('🚫 Clear Path');
if (effects.length === 0) return null;
return 'Active Effects:\n' + effects.join('\n');
}
function applyPowerUp() {
var powerUps = ['shield', 'doubleCoins', 'extraLife', 'clearPath', 'smallerShip'];
var powerUp = powerUps[Math.floor(Math.random() * powerUps.length)];
switch (powerUp) {
case 'shield':
ship.activateShield();
lastAppliedEffect = '🛡️ Shield Activated!';
break;
case 'doubleCoins':
doubleCoinsTime = 600;
lastAppliedEffect = '💰 Double Coins for 10s!';
break;
case 'extraLife':
if (health < 5) {
health++;
updateHearts();
lastAppliedEffect = '❤️ Extra Life Gained!';
} else {
lastAppliedEffect = '❤️ Life Full Already!';
}
break;
case 'clearPath':
clearPathTime = 480;
lastAppliedEffect = '🚫 Clear Path for 8s!';
// Remove existing obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
break;
case 'smallerShip':
ship.setSizeModifier(0.7, 300);
lastAppliedEffect = '⬇️ Smaller Ship for 5s!';
break;
}
}
function applyPenalty() {
var penalties = ['extraObstacles', 'healthLoss', 'stickyControls', 'scoreFreeze', 'biggerShip'];
var penalty = penalties[Math.floor(Math.random() * penalties.length)];
switch (penalty) {
case 'extraObstacles':
extraObstaclesTime = 480;
lastAppliedEffect = '⚠️ Extra Obstacles for 8s!';
break;
case 'healthLoss':
takeDamage();
lastAppliedEffect = '💔 Lost 1 Heart!';
break;
case 'stickyControls':
ship.activateStickyControls();
lastAppliedEffect = '🐌 Sticky Controls for 5s!';
break;
case 'scoreFreeze':
scoreFreezeTime = 300;
lastAppliedEffect = '❄️ Score Frozen for 5s!';
break;
case 'biggerShip':
ship.setSizeModifier(1.4, 240);
lastAppliedEffect = '⬆️ Bigger Ship for 4s!';
break;
}
}
function showEffectFeedback(effectText, isPositive) {
if (effectDisplay) {
effectDisplay.destroy();
}
effectDisplay = game.addChild(new Container());
var feedbackBg = effectDisplay.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.6,
scaleY: 0.3,
alpha: 0.8
});
var feedbackText = new Text2(effectText, {
size: 60,
fill: isPositive ? 0x00FF00 : 0xFF4444
});
feedbackText.anchor.set(0.5, 0.5);
feedbackText.x = 1024;
feedbackText.y = 1366;
effectDisplay.addChild(feedbackText);
// Auto-hide after 2 seconds
LK.setTimeout(function () {
if (effectDisplay) {
effectDisplay.destroy();
effectDisplay = null;
}
}, 2000);
}
function hideQuiz() {
quizActive = false;
if (quizContainer) {
quizContainer.destroy();
quizContainer = null;
}
}
function takeDamage() {
if (ship.isShielded) return;
health--;
updateHearts();
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
if (health <= 0) {
// Create custom game over display with statistics
var gameOverContainer = game.addChild(new Container());
var gameOverBg = gameOverContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.2,
scaleY: 1.4,
alpha: 0.95
});
var finalScore = Math.floor(distance) + crewCount * 100;
var statsText = 'GAME OVER\n\n' + 'Final Statistics:\n' + 'Score: ' + finalScore + '\n' + 'Distance: ' + Math.floor(distance) + 'm\n' + 'Crew Members: ' + crewCount + '\n' + 'Coins: ' + coinCount + '\n' + 'Level Reached: ' + level;
var statsDisplay = new Text2(statsText, {
size: 60,
fill: 0xFFFFFF
});
statsDisplay.anchor.set(0.5, 0.5);
statsDisplay.x = 1024;
statsDisplay.y = 1366;
gameOverContainer.addChild(statsDisplay);
// Show standard game over after 3 seconds
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
}
function updateHearts() {
for (var i = 0; i < hearts.length; i++) {
hearts[i].visible = i < health;
}
}
var dragActive = false;
var targetX = 1024;
game.down = function (x, y, obj) {
if (quizActive) return;
dragActive = true;
targetX = x;
};
game.move = function (x, y, obj) {
if (quizActive || !dragActive) return;
targetX = x;
};
game.up = function (x, y, obj) {
dragActive = false;
};
game.update = function () {
if (quizActive) {
// Update quiz timer
if (quizTimeRemaining > 0) {
quizTimeRemaining--;
var secondsLeft = Math.ceil(quizTimeRemaining / 60);
if (quizContainer && quizContainer.timerText) {
quizContainer.timerText.setText('Time: ' + secondsLeft);
}
if (quizTimeRemaining <= 0) {
// Time's up - apply penalty
LK.getSound('wrong').play();
applyPenalty();
hideQuiz();
nextQuizTime = LK.ticks + 300; // 5 seconds after timeout
}
}
return; // Don't update game while quiz is active
}
// Move ship towards target
if (ship.stickyTime <= 0) {
var diff = targetX - ship.x;
ship.x += diff * 0.1;
} else {
var stickyDiff = targetX - ship.x;
ship.x += stickyDiff * 0.02; // Much slower movement
}
// Keep ship in bounds
if (ship.x < 60) ship.x = 60;
if (ship.x > 1988) ship.x = 1988;
// Update distance and score
distance += gameSpeed;
updateScore();
updateLevel();
// Update distance display
distanceText.setText('Distance: ' + Math.floor(distance) + 'm');
// Progressive difficulty scaling based on distance
var difficultyMultiplier = 1 + Math.floor(distance / 500) * 0.3; // Increase difficulty every 500m
gameSpeed = Math.min(8, 2 + difficultyMultiplier); // Cap max speed at 8
// Update timers
if (doubleCoinsTime > 0) doubleCoinsTime--;
if (extraObstaclesTime > 0) extraObstaclesTime--;
if (clearPathTime > 0) clearPathTime--;
if (scoreFreezeTime > 0) scoreFreezeTime--;
// Show quiz
if (LK.ticks >= nextQuizTime) {
showQuiz();
}
// Spawn objects - increased difficulty based on distance
var baseSpawnRate = Math.max(8, 40 - Math.floor(distance / 300)); // Faster spawning every 300m distance
var obstacleSpawnRate = Math.max(5, baseSpawnRate - level * 3); // Much faster spawning at higher levels
if (LK.ticks % obstacleSpawnRate === 0) {
spawnObstacle();
// Spawn additional obstacles based on distance traveled
if (distance > 1000 && LK.ticks % (obstacleSpawnRate * 2) === 0) {
spawnObstacle(); // Extra obstacle after 1000m
}
if (distance > 2000 && LK.ticks % (obstacleSpawnRate * 3) === 0) {
spawnObstacle(); // Even more obstacles after 2000m
}
if (extraObstaclesTime > 0 && LK.ticks % Math.max(4, obstacleSpawnRate / 4) === 0) {
spawnObstacle(); // Extra obstacles during penalty
}
}
if (LK.ticks % 240 === 0) {
// Every 4 seconds - less frequent crew
spawnCrewMember();
}
if (LK.ticks % 150 === 0) {
// Every 2.5 seconds - slightly more frequent coins
spawnCoin();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.y > 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
if (ship.intersects(obstacle)) {
obstacle.destroy();
obstacles.splice(i, 1);
takeDamage();
continue;
}
}
// Update crew members
for (var i = crewMembers.length - 1; i >= 0; i--) {
var crew = crewMembers[i];
if (crew.y > 2800) {
crew.destroy();
crewMembers.splice(i, 1);
continue;
}
if (ship.intersects(crew)) {
crewCount++;
crewText.setText('Crew: ' + crewCount);
crew.destroy();
crewMembers.splice(i, 1);
LK.getSound('collect').play();
continue;
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.y > 2800) {
coin.destroy();
coins.splice(i, 1);
continue;
}
if (ship.intersects(coin)) {
var coinValue = doubleCoinsTime > 0 ? 20 : 10;
coinCount++;
coinText.setText('Coins: ' + coinCount);
if (scoreFreezeTime <= 0) {
distance += coinValue;
}
coin.destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
continue;
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
coinGraphics.rotation += 0.1;
};
return self;
});
var CrewMember = Container.expand(function () {
var self = Container.call(this);
var crewGraphics = self.attachAsset('crew', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
var pirateGraphics = self.attachAsset('pirate', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
pirateGraphics.rotation += 0.05;
};
return self;
});
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 3;
self.currentSpeed = self.baseSpeed;
self.isShielded = false;
self.shieldTime = 0;
self.stickyTime = 0;
self.isSizeModified = false;
self.sizeTime = 0;
self.originalScale = 1;
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
shieldGraphics.visible = false;
self.activateShield = function () {
self.isShielded = true;
self.shieldTime = 300;
shieldGraphics.visible = true;
};
self.activateStickyControls = function () {
self.stickyTime = 300;
};
self.setSizeModifier = function (scale, duration) {
self.isSizeModified = true;
self.sizeTime = duration;
tween(shipGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 200
});
tween(shieldGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 200
});
};
self.update = function () {
if (self.shieldTime > 0) {
self.shieldTime--;
if (self.shieldTime <= 0) {
self.isShielded = false;
shieldGraphics.visible = false;
}
}
if (self.stickyTime > 0) {
self.stickyTime--;
}
if (self.sizeTime > 0) {
self.sizeTime--;
if (self.sizeTime <= 0) {
self.isSizeModified = false;
tween(shipGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
tween(shieldGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
}
// Ship stays in place - world moves around it
};
return self;
});
var Stone = Container.expand(function () {
var self = Container.call(this);
var stoneGraphics = self.attachAsset('stone', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.update = function () {
self.y += self.speed;
stoneGraphics.rotation += 0.08;
};
return self;
});
var Wave = Container.expand(function () {
var self = Container.call(this);
var waveGraphics = self.attachAsset('wave', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.update = function () {
self.y += self.speed;
waveGraphics.scaleX = 1 + Math.sin(LK.ticks * 0.1) * 0.2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var ship;
var obstacles = [];
var crewMembers = [];
var coins = [];
var hearts = [];
var level = 1;
var health = 3;
var crewCount = 0;
var coinCount = 0;
var distance = 0;
var distanceText = new Text2('Distance: 0m', {
size: 50,
fill: 0xFFFFFF
});
distanceText.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceText);
distanceText.x = 512;
var gameSpeed = 2;
var quizActive = false;
var quizTimer = 0;
var nextQuizTime = 300; // Start first quiz after 5 seconds
var doubleCoinsTime = 0;
var extraObstaclesTime = 0;
var clearPathTime = 0;
var scoreFreezeTime = 0;
// Quiz system
var quizQuestions = [{
question: "What does FOB stand for in shipping terms?",
answers: ["Free On Board", "Freight on Boat", "Foreign Object Bureau", "Full Ocean Bill"],
correct: 0
}, {
question: "Which maritime organization sets international shipping standards?",
answers: ["WHO", "IMO", "WTO", "ILO"],
correct: 1
}, {
question: "What is the front of a ship called?",
answers: ["Stern", "Port", "Bow", "Starboard"],
correct: 2
}, {
question: "EXW means the seller delivers when goods are:",
answers: ["At seller's premises", "On the ship", "At destination", "In transit"],
correct: 0
}, {
question: "What does CIF include in shipping terms?",
answers: ["Cost only", "Insurance only", "Freight only", "Cost, Insurance, Freight"],
correct: 3
}, {
question: "What is the right side of a ship called?",
answers: ["Port", "Starboard", "Bow", "Stern"],
correct: 1
}, {
question: "DDP means the seller is responsible for:",
answers: ["Delivery at port", "Delivery duty paid", "Dock departure point", "Direct delivery purchase"],
correct: 1
}, {
question: "What does TEU stand for in container shipping?",
answers: ["Total Export Unit", "Twenty-foot Equivalent Unit", "Terminal Exchange Unit", "Transport Equipment Unit"],
correct: 1
}, {
question: "Which term means 'Carrier alongside ship'?",
answers: ["CAS", "FAS", "DAS", "PAS"],
correct: 1
}, {
question: "What is the kitchen area on a ship called?",
answers: ["Mess", "Galley", "Cabin", "Hold"],
correct: 1
}, {
question: "CFR includes which costs?",
answers: ["Cost and freight", "Cost, freight, risk", "Customs, freight, rate", "Container freight rate"],
correct: 0
}, {
question: "What does DAP stand for?",
answers: ["Delivery at place", "Dock arrival point", "Delivered at place", "Direct arrival point"],
correct: 2
}, {
question: "The left side of a ship is called:",
answers: ["Starboard", "Port", "Bow", "Stern"],
correct: 1
}, {
question: "What is a ship's maximum cargo capacity called?",
answers: ["Deadweight", "Gross tonnage", "Net tonnage", "Displacement"],
correct: 0
}, {
question: "FCA means:",
answers: ["Free carrier", "Freight cost allowance", "Full container access", "Forward cargo arrangement"],
correct: 0
}];
var currentQuiz = null;
var quizContainer = null;
var quizTimeRemaining = 600; // 10 seconds
var effectDisplay = null;
var lastAppliedEffect = '';
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.top.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
LK.gui.topRight.addChild(levelText);
var crewText = new Text2('Crew: 0', {
size: 50,
fill: 0xFFFFFF
});
crewText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(crewText);
var coinText = new Text2('Coins: 0', {
size: 50,
fill: 0xffd700
});
coinText.anchor.set(1, 1);
LK.gui.bottomRight.addChild(coinText);
// Initialize game elements
ship = game.addChild(new Ship());
ship.x = 1024;
ship.y = 2400;
// Create hearts display
for (var i = 0; i < 3; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: 150 + i * 60,
y: 100
});
hearts.push(heart);
LK.gui.topLeft.addChild(heart);
}
function updateScore() {
if (scoreFreezeTime <= 0) {
var score = Math.floor(distance) + crewCount * 100;
LK.setScore(score);
scoreText.setText('Score: ' + score);
}
}
function updateLevel() {
var newLevel = Math.floor(distance / 800) + 1; // Level up faster for increased difficulty
if (newLevel > level && newLevel <= 4) {
level = newLevel;
levelText.setText('Level: ' + level);
gameSpeed += 0.5; // Increased speed boost per level
// Change background color based on level
if (level === 2) {
game.setBackgroundColor(0xFF8C00); // Orange sunset
} else if (level === 3) {
game.setBackgroundColor(0x191970); // Dark blue night
} else if (level === 4) {
game.setBackgroundColor(0x2F4F4F); // Dark gray storm
}
}
}
function spawnObstacle() {
if (clearPathTime > 0) return;
var obstacleType = Math.floor(Math.random() * 4);
var obstacle;
switch (obstacleType) {
case 0:
obstacle = new Obstacle();
break;
case 1:
obstacle = new Pirate();
break;
case 2:
obstacle = new Wave();
break;
case 3:
obstacle = new Stone();
break;
}
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = -100;
obstacle.speed = obstacle.speed + gameSpeed + level * 0.8; // Faster obstacles at higher levels
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnCrewMember() {
var crew = new CrewMember();
crew.x = Math.random() * 1800 + 124;
crew.y = -100;
crew.speed = 3 + gameSpeed;
crewMembers.push(crew);
game.addChild(crew);
}
function spawnCoin() {
var coin = new Coin();
coin.x = Math.random() * 1800 + 124;
coin.y = -100;
coin.speed = 3 + gameSpeed;
coins.push(coin);
game.addChild(coin);
}
function showQuiz() {
if (quizActive) return;
quizActive = true;
quizTimeRemaining = 600;
currentQuiz = quizQuestions[Math.floor(Math.random() * quizQuestions.length)];
quizContainer = game.addChild(new Container());
var bg = quizContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
alpha: 0.95
});
// Show current active effects
var effectsText = getActiveEffectsText();
if (effectsText) {
var effectDisplay = new Text2(effectsText, {
size: 45,
fill: 0xFFFF00
});
effectDisplay.anchor.set(0.5, 0.5);
effectDisplay.x = 1024;
effectDisplay.y = 900;
quizContainer.addChild(effectDisplay);
}
var questionText = new Text2(currentQuiz.question, {
size: 70,
fill: 0xFFFFFF
});
questionText.anchor.set(0.5, 0.5);
questionText.x = 1024;
questionText.y = 1100;
quizContainer.addChild(questionText);
var timerText = new Text2('Time: 10', {
size: 60,
fill: 0xFFFF00
});
timerText.anchor.set(0.5, 0.5);
timerText.x = 1024;
timerText.y = 1000;
quizContainer.addChild(timerText);
quizContainer.timerText = timerText;
// Create answer buttons
quizContainer.answerButtons = [];
for (var i = 0; i < 4; i++) {
var answerContainer = new Container();
answerContainer.x = 1024;
answerContainer.y = 1300 + i * 120;
answerContainer.answerIndex = i;
var answerBg = answerContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.2,
alpha: 0.7
});
var answerText = new Text2(currentQuiz.answers[i], {
size: 50,
fill: 0xFFFFFF
});
answerText.anchor.set(0.5, 0.5);
answerContainer.addChild(answerText);
// Store index in closure to avoid reference issues
answerContainer.down = function (index) {
return function (x, y, obj) {
handleQuizAnswer(index);
};
}(i);
quizContainer.answerButtons.push(answerContainer);
quizContainer.addChild(answerContainer);
}
}
function handleQuizAnswer(answerIndex) {
if (!quizActive) return;
// Prevent multiple answers
quizActive = false;
var isCorrect = answerIndex === currentQuiz.correct;
if (isCorrect) {
LK.getSound('correct').play();
applyPowerUp();
} else {
LK.getSound('wrong').play();
applyPenalty();
}
// Show effect feedback for 2 seconds
if (lastAppliedEffect) {
showEffectFeedback(lastAppliedEffect, isCorrect);
}
hideQuiz();
nextQuizTime = LK.ticks + 300; // Every 5 seconds
}
function getActiveEffectsText() {
var effects = [];
if (ship.isShielded) effects.push('🛡️ Shield Active');
if (doubleCoinsTime > 0) effects.push('💰 Double Coins');
if (ship.isSizeModified && ship.children[0].scaleX < 1) effects.push('⬇️ Smaller Ship');
if (ship.isSizeModified && ship.children[0].scaleX > 1) effects.push('⬆️ Bigger Ship');
if (ship.stickyTime > 0) effects.push('🐌 Sticky Controls');
if (extraObstaclesTime > 0) effects.push('⚠️ Extra Obstacles');
if (scoreFreezeTime > 0) effects.push('❄️ Score Frozen');
if (clearPathTime > 0) effects.push('🚫 Clear Path');
if (effects.length === 0) return null;
return 'Active Effects:\n' + effects.join('\n');
}
function applyPowerUp() {
var powerUps = ['shield', 'doubleCoins', 'extraLife', 'clearPath', 'smallerShip'];
var powerUp = powerUps[Math.floor(Math.random() * powerUps.length)];
switch (powerUp) {
case 'shield':
ship.activateShield();
lastAppliedEffect = '🛡️ Shield Activated!';
break;
case 'doubleCoins':
doubleCoinsTime = 600;
lastAppliedEffect = '💰 Double Coins for 10s!';
break;
case 'extraLife':
if (health < 5) {
health++;
updateHearts();
lastAppliedEffect = '❤️ Extra Life Gained!';
} else {
lastAppliedEffect = '❤️ Life Full Already!';
}
break;
case 'clearPath':
clearPathTime = 480;
lastAppliedEffect = '🚫 Clear Path for 8s!';
// Remove existing obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
break;
case 'smallerShip':
ship.setSizeModifier(0.7, 300);
lastAppliedEffect = '⬇️ Smaller Ship for 5s!';
break;
}
}
function applyPenalty() {
var penalties = ['extraObstacles', 'healthLoss', 'stickyControls', 'scoreFreeze', 'biggerShip'];
var penalty = penalties[Math.floor(Math.random() * penalties.length)];
switch (penalty) {
case 'extraObstacles':
extraObstaclesTime = 480;
lastAppliedEffect = '⚠️ Extra Obstacles for 8s!';
break;
case 'healthLoss':
takeDamage();
lastAppliedEffect = '💔 Lost 1 Heart!';
break;
case 'stickyControls':
ship.activateStickyControls();
lastAppliedEffect = '🐌 Sticky Controls for 5s!';
break;
case 'scoreFreeze':
scoreFreezeTime = 300;
lastAppliedEffect = '❄️ Score Frozen for 5s!';
break;
case 'biggerShip':
ship.setSizeModifier(1.4, 240);
lastAppliedEffect = '⬆️ Bigger Ship for 4s!';
break;
}
}
function showEffectFeedback(effectText, isPositive) {
if (effectDisplay) {
effectDisplay.destroy();
}
effectDisplay = game.addChild(new Container());
var feedbackBg = effectDisplay.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 0.6,
scaleY: 0.3,
alpha: 0.8
});
var feedbackText = new Text2(effectText, {
size: 60,
fill: isPositive ? 0x00FF00 : 0xFF4444
});
feedbackText.anchor.set(0.5, 0.5);
feedbackText.x = 1024;
feedbackText.y = 1366;
effectDisplay.addChild(feedbackText);
// Auto-hide after 2 seconds
LK.setTimeout(function () {
if (effectDisplay) {
effectDisplay.destroy();
effectDisplay = null;
}
}, 2000);
}
function hideQuiz() {
quizActive = false;
if (quizContainer) {
quizContainer.destroy();
quizContainer = null;
}
}
function takeDamage() {
if (ship.isShielded) return;
health--;
updateHearts();
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 500);
if (health <= 0) {
// Create custom game over display with statistics
var gameOverContainer = game.addChild(new Container());
var gameOverBg = gameOverContainer.attachAsset('quizBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 1.2,
scaleY: 1.4,
alpha: 0.95
});
var finalScore = Math.floor(distance) + crewCount * 100;
var statsText = 'GAME OVER\n\n' + 'Final Statistics:\n' + 'Score: ' + finalScore + '\n' + 'Distance: ' + Math.floor(distance) + 'm\n' + 'Crew Members: ' + crewCount + '\n' + 'Coins: ' + coinCount + '\n' + 'Level Reached: ' + level;
var statsDisplay = new Text2(statsText, {
size: 60,
fill: 0xFFFFFF
});
statsDisplay.anchor.set(0.5, 0.5);
statsDisplay.x = 1024;
statsDisplay.y = 1366;
gameOverContainer.addChild(statsDisplay);
// Show standard game over after 3 seconds
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
}
function updateHearts() {
for (var i = 0; i < hearts.length; i++) {
hearts[i].visible = i < health;
}
}
var dragActive = false;
var targetX = 1024;
game.down = function (x, y, obj) {
if (quizActive) return;
dragActive = true;
targetX = x;
};
game.move = function (x, y, obj) {
if (quizActive || !dragActive) return;
targetX = x;
};
game.up = function (x, y, obj) {
dragActive = false;
};
game.update = function () {
if (quizActive) {
// Update quiz timer
if (quizTimeRemaining > 0) {
quizTimeRemaining--;
var secondsLeft = Math.ceil(quizTimeRemaining / 60);
if (quizContainer && quizContainer.timerText) {
quizContainer.timerText.setText('Time: ' + secondsLeft);
}
if (quizTimeRemaining <= 0) {
// Time's up - apply penalty
LK.getSound('wrong').play();
applyPenalty();
hideQuiz();
nextQuizTime = LK.ticks + 300; // 5 seconds after timeout
}
}
return; // Don't update game while quiz is active
}
// Move ship towards target
if (ship.stickyTime <= 0) {
var diff = targetX - ship.x;
ship.x += diff * 0.1;
} else {
var stickyDiff = targetX - ship.x;
ship.x += stickyDiff * 0.02; // Much slower movement
}
// Keep ship in bounds
if (ship.x < 60) ship.x = 60;
if (ship.x > 1988) ship.x = 1988;
// Update distance and score
distance += gameSpeed;
updateScore();
updateLevel();
// Update distance display
distanceText.setText('Distance: ' + Math.floor(distance) + 'm');
// Progressive difficulty scaling based on distance
var difficultyMultiplier = 1 + Math.floor(distance / 500) * 0.3; // Increase difficulty every 500m
gameSpeed = Math.min(8, 2 + difficultyMultiplier); // Cap max speed at 8
// Update timers
if (doubleCoinsTime > 0) doubleCoinsTime--;
if (extraObstaclesTime > 0) extraObstaclesTime--;
if (clearPathTime > 0) clearPathTime--;
if (scoreFreezeTime > 0) scoreFreezeTime--;
// Show quiz
if (LK.ticks >= nextQuizTime) {
showQuiz();
}
// Spawn objects - increased difficulty based on distance
var baseSpawnRate = Math.max(8, 40 - Math.floor(distance / 300)); // Faster spawning every 300m distance
var obstacleSpawnRate = Math.max(5, baseSpawnRate - level * 3); // Much faster spawning at higher levels
if (LK.ticks % obstacleSpawnRate === 0) {
spawnObstacle();
// Spawn additional obstacles based on distance traveled
if (distance > 1000 && LK.ticks % (obstacleSpawnRate * 2) === 0) {
spawnObstacle(); // Extra obstacle after 1000m
}
if (distance > 2000 && LK.ticks % (obstacleSpawnRate * 3) === 0) {
spawnObstacle(); // Even more obstacles after 2000m
}
if (extraObstaclesTime > 0 && LK.ticks % Math.max(4, obstacleSpawnRate / 4) === 0) {
spawnObstacle(); // Extra obstacles during penalty
}
}
if (LK.ticks % 240 === 0) {
// Every 4 seconds - less frequent crew
spawnCrewMember();
}
if (LK.ticks % 150 === 0) {
// Every 2.5 seconds - slightly more frequent coins
spawnCoin();
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.y > 2800) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
if (ship.intersects(obstacle)) {
obstacle.destroy();
obstacles.splice(i, 1);
takeDamage();
continue;
}
}
// Update crew members
for (var i = crewMembers.length - 1; i >= 0; i--) {
var crew = crewMembers[i];
if (crew.y > 2800) {
crew.destroy();
crewMembers.splice(i, 1);
continue;
}
if (ship.intersects(crew)) {
crewCount++;
crewText.setText('Crew: ' + crewCount);
crew.destroy();
crewMembers.splice(i, 1);
LK.getSound('collect').play();
continue;
}
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.y > 2800) {
coin.destroy();
coins.splice(i, 1);
continue;
}
if (ship.intersects(coin)) {
var coinValue = doubleCoinsTime > 0 ? 20 : 10;
coinCount++;
coinText.setText('Coins: ' + coinCount);
if (scoreFreezeTime <= 0) {
distance += coinValue;
}
coin.destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
continue;
}
}
};