User prompt
As you gain more distance, the game should get faster, more difficult, and more items should fall. Also, when the quiz appears, the game must pause and reduce the number of questions frequent for 5 seconds. Also, record the distance traveled on the screen. And mention about it in the end i mean show score and distance crews obviosly statistics.
User prompt
Add more falling obstacles, such as pirates, waves, and stones. The scoreboard should display integers. Pause the game when the quiz screen opens. Also, add a section on the screen that counts coins. This will make the game much faster. Don't ask the same questions over and over again. So, add more questions.
User prompt
make it game harder, fix score tab, when the quiz appears pause the game and show which penalty or power up applied
User prompt
ship went away fix it also fix quiz mechanic
Code edit (1 edits merged)
Please save this source code
User prompt
Maritime Quiz Navigator
Initial prompt
Create an endless 2D maritime-themed game. create a menu before starting the game. The player controls a ship that automatically moves forward and can steer left or right to avoid obstacles like rocks, waves, or pirates. At certain intervals, a quiz screen should appear with a maritime-related question. You can mention Incoterms and other similar concepts in the questions. Simply having maritime-related topics, such as ship sections and components, maritime-related organizations, etc., is sufficient. When the quiz appears, count down from 10. Make the questions four-choice. And there is only 1 correct answer for gaining power-up all of the other answers wrong and causes penalty If the player answers correctly, they gain a power-up power ups: Shield (invincible for 5s) Double Coins (double coins for 10s) Extra Life – Extra 1 life even its full Clear Path – Remove all obstacles on screen instantly. Smaller ship - for 5 seconds. If the player answers incorrectly, they receive a penalty Penalty Ideas Extra Obstacles – Spawn more obstacles for 8 seconds. Lose score Lose coin Health loss (-1 heart) Sticky Controls – Delay in response for 5 seconds. Score Freeze – No points for 5 seconds. Bigger Ship – for 4 seconds. Add some crew members for collecting so it can increase score, scores will calculate as distance + crew members times 100. show a bar for collected crew members on the top While the crew recruitments feature is only for scoring, it also adds coins to the game. Display the score at the end of the game. The game should include a leveling system: as the player levels up, the ship design upgrades and the background evolves (day, night, storm, open sea). The game should feel casual, easy to play, but addictive. Please generate the initial prototype logic, placeholders for assets, and a basic scoring system. Game Flow Ship moves forward automatically at increasing speed as player progresses through levels Player steers left/right using touch controls to avoid randomly generated obstacles (rocks, waves, pirates) Quiz questions appear every 15-20 seconds, pausing the action temporarily Correct quiz answers grant random power-ups Incorrect answers impose penalties Three-heart health system - lose hearts by hitting obstacles or wrong quiz answers. However, the lives gained from correct answers can exceed the 3 heart limit. Leveling system triggered by distance milestones: Level 1 (wooden boat, day), Level 2 (merchant ship, sunset), Level 3 (warship, night), Level 4 (modern vessel, storm) Score increases continuously based on distance traveled plus bonus points for correct quiz answers Game ends when all hearts are lost, displaying final score and level reached
/****
* 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;
}
}
};