/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('coinCollect').play();
// Add coin to storage
var currentCoins = storage.coins || 0;
var coinValue = 1;
// Check if coin multiplier is active
if (coinMultiplierGamesLeft > 0) {
coinValue = 2;
}
storage.coins = currentCoins + coinValue;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
self.destroy();
};
return self;
});
var Fish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('fish', {
anchorX: 0.5,
anchorY: 0.5
});
self.jumpSpeed = -8;
self.gravity = 0.4;
self.velocityY = self.jumpSpeed;
self.startY = 0;
self.jump = function () {
self.velocityY = self.jumpSpeed;
LK.getSound('fishJump').play();
};
self.update = function () {
self.velocityY += self.gravity;
self.y += self.velocityY;
if (self.y > self.startY + 100) {
self.destroy();
}
};
return self;
});
var Frog = Container.expand(function (frogType) {
var self = Container.call(this);
self.frogType = frogType || 'normal';
var frogGraphics = self.attachAsset('frog', {
anchorX: 0.5,
anchorY: 0.5
});
self.isJumping = false;
self.currentStone = null;
self.jumpSpeed = 8;
self.targetX = 0;
self.targetY = 0;
self.doubleJumpUsed = false;
// Apply frog abilities based on type
if (self.frogType === 'fast') {
self.jumpSpeed = 12;
frogGraphics.tint = 0xff6b6b;
} else if (self.frogType === 'jumper') {
self.jumpSpeed = 10;
frogGraphics.tint = 0x4ecdc4;
} else if (self.frogType === 'double') {
frogGraphics.tint = 0xffe66d;
}
self.jumpTo = function (stone) {
if (self.isJumping) return;
self.isJumping = true;
self.targetX = stone.x;
self.targetY = stone.y - 50;
if (self.currentStone) {
self.currentStone.startSinking();
}
self.currentStone = stone;
LK.getSound('jump').play();
var jumpDistance = Math.sqrt(Math.pow(self.targetX - self.x, 2) + Math.pow(self.targetY - self.y, 2));
var jumpDuration = jumpDistance / self.jumpSpeed * 16.67; // Convert to ms
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: jumpDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isJumping = false;
if (self.frogType === 'double') {
self.doubleJumpUsed = false;
}
if (stone.stoneType === 'slippery') {
var slipAmount = (Math.random() - 0.5) * 40;
self.x += slipAmount;
}
}
});
};
self.doubleJump = function (stone) {
if (self.frogType !== 'double' || self.doubleJumpUsed) return;
self.doubleJumpUsed = true;
self.isJumping = false; // Reset jumping state to allow new jump
self.jumpTo(stone);
};
return self;
});
var Stone = Container.expand(function (stoneType) {
var self = Container.call(this);
self.stoneType = stoneType || 'regular';
self.sinkTimer = 0;
self.maxSinkTime = 180; // 3 seconds at 60fps
self.isOccupied = false;
self.originalY = 0;
self.moveSpeed = 0;
var assetName = 'stone';
if (self.stoneType === 'moving') {
assetName = 'movingStone';
self.moveSpeed = Math.random() * 2 - 1; // -1 to 1
} else if (self.stoneType === 'slippery') {
assetName = 'slipperyStone';
}
var stoneGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.startSinking = function () {
self.isOccupied = true;
self.sinkTimer = 0;
};
self.update = function () {
if (self.isOccupied) {
// Stone disappears immediately when frog is on it
self.destroy();
return;
}
if (self.stoneType === 'moving') {
self.x += self.moveSpeed;
if (self.x < 60 || self.x > 1988) {
self.moveSpeed *= -1;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0077BE
});
/****
* Game Code
****/
var gameStarted = false;
var titleTxt = null;
var playButton = null;
var storeButton = null;
var showingStore = false;
var storeItems = [];
var storeTexts = [];
var waterBackground = game.addChild(LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var stones = [];
var fishes = [];
var coins = [];
var currentFrogType = storage.selectedFrog || 'normal';
var coinMultiplierGamesLeft = storage.coinMultiplierGamesLeft || 0;
var frogPrices = {
'normal': 0,
'fast': 50,
'jumper': 75,
'double': 100
};
var frogNames = {
'normal': 'Normal Frog',
'fast': 'Fast Frog',
'jumper': 'High Jumper',
'double': 'Double Jump'
};
var powerupPrices = {
'coinMultiplier': 50
};
var powerupNames = {
'coinMultiplier': 'Coin Multiplier x2'
};
var frog = game.addChild(new Frog(currentFrogType));
var level = 1;
var crossingCount = 0;
var fishSpawnTimer = 0;
var coinSpawnTimer = 0;
var maxDistance = 0;
var startY = 2650;
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
// Level display
var levelTxt = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
// Coins display
var coinsTxt = new Text2('Coins: ' + (storage.coins || 0), {
size: 50,
fill: 0xffd700
});
coinsTxt.anchor.set(0, 0);
coinsTxt.x = 120;
coinsTxt.y = 120;
LK.gui.topLeft.addChild(coinsTxt);
// Distance display
var distanceTxt = new Text2('Distance: 0m', {
size: 50,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(1, 0);
distanceTxt.x = -20;
distanceTxt.y = 60;
LK.gui.topRight.addChild(distanceTxt);
function createStoneRow(y, count) {
var stoneSpacing = 2048 / (count + 1);
for (var i = 0; i < count; i++) {
var stoneType = 'regular';
var rand = Math.random();
// Increase probability of special stones with level
var movingChance = Math.min(0.4, 0.1 + level * 0.05);
var slipperyChance = Math.min(0.3, 0.1 + level * 0.04);
if (level > 1 && rand < movingChance) {
stoneType = 'moving';
} else if (level > 2 && rand < movingChance + slipperyChance) {
stoneType = 'slippery';
}
var stone = new Stone(stoneType);
stone.x = stoneSpacing * (i + 1) + (Math.random() - 0.5) * 100;
stone.y = y;
stone.originalY = y;
stones.push(stone);
game.addChild(stone);
// Spawn coin randomly on some stones
if (Math.random() < 0.3) {
var coin = new Coin();
coin.x = stone.x;
coin.y = stone.y - 40;
coins.push(coin);
game.addChild(coin);
}
}
}
function initializeLevel() {
// Clear existing stones
for (var i = stones.length - 1; i >= 0; i--) {
stones[i].destroy();
}
stones = [];
// Create stone rows
var rows = 6 + level;
var rowSpacing = 2200 / rows;
for (var row = 0; row < rows; row++) {
var y = 2600 - row * rowSpacing;
var stoneCount = 3 + Math.floor(Math.random() * 3);
createStoneRow(y, stoneCount);
}
// Position frog at start
frog.x = 1024;
frog.y = 2650;
frog.currentStone = null;
frog.isJumping = false;
}
function spawnFish() {
if (Math.random() < 0.3) {
var fish = new Fish();
fish.x = Math.random() * 1800 + 124;
fish.y = Math.random() * 1800 + 400;
fish.startY = fish.y;
fish.jump();
fishes.push(fish);
game.addChild(fish);
}
}
function checkWinCondition() {
// Count remaining stones (not destroyed or sinking)
var remainingStones = 0;
for (var i = 0; i < stones.length; i++) {
if (!stones[i].destroyed && !stones[i].isOccupied) {
remainingStones++;
}
}
// Check if only one stone remains
if (remainingStones === 1) {
crossingCount++;
LK.setScore(LK.getScore() + 100 + level * 20);
level++;
levelTxt.setText('Level: ' + level);
// Clear existing coins
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].destroy();
}
coins = [];
// Clear existing fish
for (var i = fishes.length - 1; i >= 0; i--) {
fishes[i].destroy();
}
fishes = [];
initializeLevel();
}
}
function checkGameOver() {
// Check if frog fell in water
if (frog.y > 2650 && !frog.isJumping) {
// Decrement coin multiplier games
if (coinMultiplierGamesLeft > 0) {
coinMultiplierGamesLeft--;
storage.coinMultiplierGamesLeft = coinMultiplierGamesLeft;
}
LK.getSound('splash').play();
LK.showGameOver();
return;
}
// Check fish collision
for (var i = fishes.length - 1; i >= 0; i--) {
var fish = fishes[i];
if (frog.intersects(fish)) {
// Decrement coin multiplier games
if (coinMultiplierGamesLeft > 0) {
coinMultiplierGamesLeft--;
storage.coinMultiplierGamesLeft = coinMultiplierGamesLeft;
}
LK.getSound('splash').play();
LK.showGameOver();
return;
}
}
}
function showStartMenu() {
// Create title text
titleTxt = new Text2('FROGGY RIVER RUSH', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 1024;
titleTxt.y = 800;
game.addChild(titleTxt);
// Create play button
playButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1200
}));
var playTxt = new Text2('PLAY', {
size: 80,
fill: 0x000000
});
playTxt.anchor.set(0.5, 0.5);
playTxt.x = 1024;
playTxt.y = 1200;
game.addChild(playTxt);
// Create store button
storeButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400
}));
var storeTxt = new Text2('STORE', {
size: 80,
fill: 0x000000
});
storeTxt.anchor.set(0.5, 0.5);
storeTxt.x = 1024;
storeTxt.y = 1400;
game.addChild(storeTxt);
}
function showStore() {
showingStore = true;
if (titleTxt) titleTxt.destroy();
if (playButton) playButton.destroy();
if (storeButton) storeButton.destroy();
// Remove menu text by destroying all text children except UI
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
if (gameChildren[i] instanceof Text2) {
gameChildren[i].destroy();
}
}
// Create store title
var storeTitleTxt = new Text2('FROG STORE', {
size: 100,
fill: 0xFFFFFF
});
storeTitleTxt.anchor.set(0.5, 0.5);
storeTitleTxt.x = 1024;
storeTitleTxt.y = 400;
game.addChild(storeTitleTxt);
storeTexts.push(storeTitleTxt);
// Show coins
var coinsDisplayTxt = new Text2('Coins: ' + (storage.coins || 0), {
size: 60,
fill: 0xffd700
});
coinsDisplayTxt.anchor.set(0.5, 0.5);
coinsDisplayTxt.x = 1024;
coinsDisplayTxt.y = 500;
game.addChild(coinsDisplayTxt);
storeTexts.push(coinsDisplayTxt);
// Create frog items
var frogTypes = ['normal', 'fast', 'double'];
for (var i = 0; i < frogTypes.length; i++) {
var frogType = frogTypes[i];
var yPos = 700 + i * 200;
// Create frog preview
var frogPreview = game.addChild(LK.getAsset('frog', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: yPos
}));
if (frogType === 'fast') {
frogPreview.tint = 0xff6b6b;
} else if (frogType === 'jumper') {
frogPreview.tint = 0x4ecdc4;
} else if (frogType === 'double') {
frogPreview.tint = 0xffe66d;
}
storeItems.push(frogPreview);
// Create name and price text
var isOwned = storage.ownedFrogs && storage.ownedFrogs[frogType];
var isSelected = currentFrogType === frogType;
var price = frogPrices[frogType];
var statusText = '';
if (isSelected) {
statusText = ' (SELECTED)';
} else if (isOwned || price === 0) {
statusText = ' (OWNED)';
} else {
statusText = ' - ' + price + ' coins';
}
var frogNameTxt = new Text2(frogNames[frogType] + statusText, {
size: 50,
fill: isSelected ? 0x00ff00 : 0xFFFFFF
});
frogNameTxt.anchor.set(0, 0.5);
frogNameTxt.x = 500;
frogNameTxt.y = yPos;
game.addChild(frogNameTxt);
storeTexts.push(frogNameTxt);
// Store frog type for purchase detection
frogPreview.frogType = frogType;
}
// Create powerup items
var powerupTypes = ['coinMultiplier'];
for (var i = 0; i < powerupTypes.length; i++) {
var powerupType = powerupTypes[i];
var yPos = 1400 + i * 200;
// Create powerup preview (using coin asset)
var powerupPreview = game.addChild(LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: yPos
}));
powerupPreview.tint = 0x00ff00; // Green tint for powerups
storeItems.push(powerupPreview);
// Create name and price text
var price = powerupPrices[powerupType];
var gamesLeft = storage.coinMultiplierGamesLeft || 0;
var statusText = '';
if (powerupType === 'coinMultiplier' && gamesLeft > 0) {
statusText = ' (' + gamesLeft + ' games left)';
} else {
statusText = ' - ' + price + ' coins';
}
var powerupNameTxt = new Text2(powerupNames[powerupType] + statusText, {
size: 50,
fill: powerupType === 'coinMultiplier' && gamesLeft > 0 ? 0x00ff00 : 0xFFFFFF
});
powerupNameTxt.anchor.set(0, 0.5);
powerupNameTxt.x = 500;
powerupNameTxt.y = yPos;
game.addChild(powerupNameTxt);
storeTexts.push(powerupNameTxt);
// Store powerup type for purchase detection
powerupPreview.powerupType = powerupType;
}
// Create back button
var backButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200
}));
var backTxt = new Text2('BACK', {
size: 80,
fill: 0x000000
});
backTxt.anchor.set(0.5, 0.5);
backTxt.x = 1024;
backTxt.y = 2200;
game.addChild(backTxt);
storeItems.push(backButton);
storeTexts.push(backTxt);
backButton.isBackButton = true;
}
function startGame() {
gameStarted = true;
showingStore = false;
// Initialize coin multiplier from storage
coinMultiplierGamesLeft = storage.coinMultiplierGamesLeft || 0;
if (titleTxt) titleTxt.destroy();
if (playButton) playButton.destroy();
if (storeButton) storeButton.destroy();
// Clean up store items
for (var i = 0; i < storeItems.length; i++) {
storeItems[i].destroy();
}
storeItems = [];
// Clean up store texts
for (var i = 0; i < storeTexts.length; i++) {
storeTexts[i].destroy();
}
storeTexts = [];
// Remove play text by destroying all text children except UI
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
if (gameChildren[i] instanceof Text2) {
gameChildren[i].destroy();
}
}
initializeLevel();
}
// Show start menu instead of initializing level
showStartMenu();
game.down = function (x, y, obj) {
if (!gameStarted && !showingStore) {
// Check if play button was clicked
if (playButton) {
var distance = Math.sqrt(Math.pow(x - playButton.x, 2) + Math.pow(y - playButton.y, 2));
if (distance < 100) {
startGame();
return;
}
}
// Check if store button was clicked
if (storeButton) {
var distance = Math.sqrt(Math.pow(x - storeButton.x, 2) + Math.pow(y - storeButton.y, 2));
if (distance < 100) {
showStore();
return;
}
}
return;
}
if (showingStore) {
// Handle store interactions
for (var i = 0; i < storeItems.length; i++) {
var item = storeItems[i];
var distance = Math.sqrt(Math.pow(x - item.x, 2) + Math.pow(y - item.y, 2));
if (distance < 100) {
if (item.isBackButton) {
showingStore = false;
// Clean up store
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStartMenu();
return;
} else if (item.frogType) {
// Try to buy/select frog
var frogType = item.frogType;
var price = frogPrices[frogType];
var isOwned = storage.ownedFrogs && storage.ownedFrogs[frogType];
var currentCoins = storage.coins || 0;
if (price === 0 || isOwned) {
// Select this frog
storage.selectedFrog = frogType;
currentFrogType = frogType;
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
} else if (currentCoins >= price) {
// Buy frog
storage.coins = currentCoins - price;
if (!storage.ownedFrogs) storage.ownedFrogs = {};
storage.ownedFrogs[frogType] = true;
storage.selectedFrog = frogType;
currentFrogType = frogType;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
}
} else if (item.powerupType) {
// Try to buy powerup
var powerupType = item.powerupType;
var price = powerupPrices[powerupType];
var currentCoins = storage.coins || 0;
if (powerupType === 'coinMultiplier' && currentCoins >= price) {
// Buy coin multiplier for 3 games
storage.coins = currentCoins - price;
storage.coinMultiplierGamesLeft = 3;
coinMultiplierGamesLeft = 3;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
}
}
break;
}
}
return;
}
if (frog.isJumping && frog.frogType !== 'double') return;
if (frog.isJumping && frog.frogType === 'double' && frog.doubleJumpUsed) return;
var nearestStone = null;
var nearestDistance = Infinity;
for (var i = 0; i < stones.length; i++) {
var stone = stones[i];
var distance = Math.sqrt(Math.pow(x - stone.x, 2) + Math.pow(y - stone.y, 2));
if (distance < 150 && distance < nearestDistance) {
nearestDistance = distance;
nearestStone = stone;
}
}
if (nearestStone && !nearestStone.isOccupied) {
if (frog.isJumping && frog.frogType === 'double') {
frog.doubleJump(nearestStone);
} else {
frog.jumpTo(nearestStone);
}
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Score: ' + LK.getScore());
}
};
game.update = function () {
if (!gameStarted) return;
// Clean up destroyed stones
for (var i = stones.length - 1; i >= 0; i--) {
if (stones[i].destroyed) {
stones.splice(i, 1);
}
}
// Clean up destroyed fish
for (var i = fishes.length - 1; i >= 0; i--) {
if (fishes[i].destroyed) {
fishes.splice(i, 1);
}
}
// Clean up destroyed coins
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i].destroyed) {
coins.splice(i, 1);
}
}
// Check coin collection
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (frog.intersects(coin) && !coin.collected) {
coin.collect();
coins.splice(i, 1);
}
}
// Update distance traveled
var currentDistance = Math.max(0, Math.floor((startY - frog.y) / 10));
if (currentDistance > maxDistance) {
maxDistance = currentDistance;
storage.maxDistance = maxDistance;
}
distanceTxt.setText('Distance: ' + currentDistance + 'm');
// Spawn fish periodically - more fish at higher levels
fishSpawnTimer++;
var fishSpawnRate = Math.max(30, 120 - level * 8); // Minimum 30 frames between spawns
if (fishSpawnTimer > fishSpawnRate) {
fishSpawnTimer = 0;
// Spawn multiple fish at higher levels
var fishCount = Math.min(3, Math.floor(level / 3) + 1);
for (var f = 0; f < fishCount; f++) {
spawnFish();
}
}
checkWinCondition();
checkGameOver();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('coinCollect').play();
// Add coin to storage
var currentCoins = storage.coins || 0;
var coinValue = 1;
// Check if coin multiplier is active
if (coinMultiplierGamesLeft > 0) {
coinValue = 2;
}
storage.coins = currentCoins + coinValue;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
self.destroy();
};
return self;
});
var Fish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('fish', {
anchorX: 0.5,
anchorY: 0.5
});
self.jumpSpeed = -8;
self.gravity = 0.4;
self.velocityY = self.jumpSpeed;
self.startY = 0;
self.jump = function () {
self.velocityY = self.jumpSpeed;
LK.getSound('fishJump').play();
};
self.update = function () {
self.velocityY += self.gravity;
self.y += self.velocityY;
if (self.y > self.startY + 100) {
self.destroy();
}
};
return self;
});
var Frog = Container.expand(function (frogType) {
var self = Container.call(this);
self.frogType = frogType || 'normal';
var frogGraphics = self.attachAsset('frog', {
anchorX: 0.5,
anchorY: 0.5
});
self.isJumping = false;
self.currentStone = null;
self.jumpSpeed = 8;
self.targetX = 0;
self.targetY = 0;
self.doubleJumpUsed = false;
// Apply frog abilities based on type
if (self.frogType === 'fast') {
self.jumpSpeed = 12;
frogGraphics.tint = 0xff6b6b;
} else if (self.frogType === 'jumper') {
self.jumpSpeed = 10;
frogGraphics.tint = 0x4ecdc4;
} else if (self.frogType === 'double') {
frogGraphics.tint = 0xffe66d;
}
self.jumpTo = function (stone) {
if (self.isJumping) return;
self.isJumping = true;
self.targetX = stone.x;
self.targetY = stone.y - 50;
if (self.currentStone) {
self.currentStone.startSinking();
}
self.currentStone = stone;
LK.getSound('jump').play();
var jumpDistance = Math.sqrt(Math.pow(self.targetX - self.x, 2) + Math.pow(self.targetY - self.y, 2));
var jumpDuration = jumpDistance / self.jumpSpeed * 16.67; // Convert to ms
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: jumpDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isJumping = false;
if (self.frogType === 'double') {
self.doubleJumpUsed = false;
}
if (stone.stoneType === 'slippery') {
var slipAmount = (Math.random() - 0.5) * 40;
self.x += slipAmount;
}
}
});
};
self.doubleJump = function (stone) {
if (self.frogType !== 'double' || self.doubleJumpUsed) return;
self.doubleJumpUsed = true;
self.isJumping = false; // Reset jumping state to allow new jump
self.jumpTo(stone);
};
return self;
});
var Stone = Container.expand(function (stoneType) {
var self = Container.call(this);
self.stoneType = stoneType || 'regular';
self.sinkTimer = 0;
self.maxSinkTime = 180; // 3 seconds at 60fps
self.isOccupied = false;
self.originalY = 0;
self.moveSpeed = 0;
var assetName = 'stone';
if (self.stoneType === 'moving') {
assetName = 'movingStone';
self.moveSpeed = Math.random() * 2 - 1; // -1 to 1
} else if (self.stoneType === 'slippery') {
assetName = 'slipperyStone';
}
var stoneGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.startSinking = function () {
self.isOccupied = true;
self.sinkTimer = 0;
};
self.update = function () {
if (self.isOccupied) {
// Stone disappears immediately when frog is on it
self.destroy();
return;
}
if (self.stoneType === 'moving') {
self.x += self.moveSpeed;
if (self.x < 60 || self.x > 1988) {
self.moveSpeed *= -1;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0077BE
});
/****
* Game Code
****/
var gameStarted = false;
var titleTxt = null;
var playButton = null;
var storeButton = null;
var showingStore = false;
var storeItems = [];
var storeTexts = [];
var waterBackground = game.addChild(LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var stones = [];
var fishes = [];
var coins = [];
var currentFrogType = storage.selectedFrog || 'normal';
var coinMultiplierGamesLeft = storage.coinMultiplierGamesLeft || 0;
var frogPrices = {
'normal': 0,
'fast': 50,
'jumper': 75,
'double': 100
};
var frogNames = {
'normal': 'Normal Frog',
'fast': 'Fast Frog',
'jumper': 'High Jumper',
'double': 'Double Jump'
};
var powerupPrices = {
'coinMultiplier': 50
};
var powerupNames = {
'coinMultiplier': 'Coin Multiplier x2'
};
var frog = game.addChild(new Frog(currentFrogType));
var level = 1;
var crossingCount = 0;
var fishSpawnTimer = 0;
var coinSpawnTimer = 0;
var maxDistance = 0;
var startY = 2650;
// Score display
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
// Level display
var levelTxt = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
// Coins display
var coinsTxt = new Text2('Coins: ' + (storage.coins || 0), {
size: 50,
fill: 0xffd700
});
coinsTxt.anchor.set(0, 0);
coinsTxt.x = 120;
coinsTxt.y = 120;
LK.gui.topLeft.addChild(coinsTxt);
// Distance display
var distanceTxt = new Text2('Distance: 0m', {
size: 50,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(1, 0);
distanceTxt.x = -20;
distanceTxt.y = 60;
LK.gui.topRight.addChild(distanceTxt);
function createStoneRow(y, count) {
var stoneSpacing = 2048 / (count + 1);
for (var i = 0; i < count; i++) {
var stoneType = 'regular';
var rand = Math.random();
// Increase probability of special stones with level
var movingChance = Math.min(0.4, 0.1 + level * 0.05);
var slipperyChance = Math.min(0.3, 0.1 + level * 0.04);
if (level > 1 && rand < movingChance) {
stoneType = 'moving';
} else if (level > 2 && rand < movingChance + slipperyChance) {
stoneType = 'slippery';
}
var stone = new Stone(stoneType);
stone.x = stoneSpacing * (i + 1) + (Math.random() - 0.5) * 100;
stone.y = y;
stone.originalY = y;
stones.push(stone);
game.addChild(stone);
// Spawn coin randomly on some stones
if (Math.random() < 0.3) {
var coin = new Coin();
coin.x = stone.x;
coin.y = stone.y - 40;
coins.push(coin);
game.addChild(coin);
}
}
}
function initializeLevel() {
// Clear existing stones
for (var i = stones.length - 1; i >= 0; i--) {
stones[i].destroy();
}
stones = [];
// Create stone rows
var rows = 6 + level;
var rowSpacing = 2200 / rows;
for (var row = 0; row < rows; row++) {
var y = 2600 - row * rowSpacing;
var stoneCount = 3 + Math.floor(Math.random() * 3);
createStoneRow(y, stoneCount);
}
// Position frog at start
frog.x = 1024;
frog.y = 2650;
frog.currentStone = null;
frog.isJumping = false;
}
function spawnFish() {
if (Math.random() < 0.3) {
var fish = new Fish();
fish.x = Math.random() * 1800 + 124;
fish.y = Math.random() * 1800 + 400;
fish.startY = fish.y;
fish.jump();
fishes.push(fish);
game.addChild(fish);
}
}
function checkWinCondition() {
// Count remaining stones (not destroyed or sinking)
var remainingStones = 0;
for (var i = 0; i < stones.length; i++) {
if (!stones[i].destroyed && !stones[i].isOccupied) {
remainingStones++;
}
}
// Check if only one stone remains
if (remainingStones === 1) {
crossingCount++;
LK.setScore(LK.getScore() + 100 + level * 20);
level++;
levelTxt.setText('Level: ' + level);
// Clear existing coins
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].destroy();
}
coins = [];
// Clear existing fish
for (var i = fishes.length - 1; i >= 0; i--) {
fishes[i].destroy();
}
fishes = [];
initializeLevel();
}
}
function checkGameOver() {
// Check if frog fell in water
if (frog.y > 2650 && !frog.isJumping) {
// Decrement coin multiplier games
if (coinMultiplierGamesLeft > 0) {
coinMultiplierGamesLeft--;
storage.coinMultiplierGamesLeft = coinMultiplierGamesLeft;
}
LK.getSound('splash').play();
LK.showGameOver();
return;
}
// Check fish collision
for (var i = fishes.length - 1; i >= 0; i--) {
var fish = fishes[i];
if (frog.intersects(fish)) {
// Decrement coin multiplier games
if (coinMultiplierGamesLeft > 0) {
coinMultiplierGamesLeft--;
storage.coinMultiplierGamesLeft = coinMultiplierGamesLeft;
}
LK.getSound('splash').play();
LK.showGameOver();
return;
}
}
}
function showStartMenu() {
// Create title text
titleTxt = new Text2('FROGGY RIVER RUSH', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 1024;
titleTxt.y = 800;
game.addChild(titleTxt);
// Create play button
playButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1200
}));
var playTxt = new Text2('PLAY', {
size: 80,
fill: 0x000000
});
playTxt.anchor.set(0.5, 0.5);
playTxt.x = 1024;
playTxt.y = 1200;
game.addChild(playTxt);
// Create store button
storeButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400
}));
var storeTxt = new Text2('STORE', {
size: 80,
fill: 0x000000
});
storeTxt.anchor.set(0.5, 0.5);
storeTxt.x = 1024;
storeTxt.y = 1400;
game.addChild(storeTxt);
}
function showStore() {
showingStore = true;
if (titleTxt) titleTxt.destroy();
if (playButton) playButton.destroy();
if (storeButton) storeButton.destroy();
// Remove menu text by destroying all text children except UI
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
if (gameChildren[i] instanceof Text2) {
gameChildren[i].destroy();
}
}
// Create store title
var storeTitleTxt = new Text2('FROG STORE', {
size: 100,
fill: 0xFFFFFF
});
storeTitleTxt.anchor.set(0.5, 0.5);
storeTitleTxt.x = 1024;
storeTitleTxt.y = 400;
game.addChild(storeTitleTxt);
storeTexts.push(storeTitleTxt);
// Show coins
var coinsDisplayTxt = new Text2('Coins: ' + (storage.coins || 0), {
size: 60,
fill: 0xffd700
});
coinsDisplayTxt.anchor.set(0.5, 0.5);
coinsDisplayTxt.x = 1024;
coinsDisplayTxt.y = 500;
game.addChild(coinsDisplayTxt);
storeTexts.push(coinsDisplayTxt);
// Create frog items
var frogTypes = ['normal', 'fast', 'double'];
for (var i = 0; i < frogTypes.length; i++) {
var frogType = frogTypes[i];
var yPos = 700 + i * 200;
// Create frog preview
var frogPreview = game.addChild(LK.getAsset('frog', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: yPos
}));
if (frogType === 'fast') {
frogPreview.tint = 0xff6b6b;
} else if (frogType === 'jumper') {
frogPreview.tint = 0x4ecdc4;
} else if (frogType === 'double') {
frogPreview.tint = 0xffe66d;
}
storeItems.push(frogPreview);
// Create name and price text
var isOwned = storage.ownedFrogs && storage.ownedFrogs[frogType];
var isSelected = currentFrogType === frogType;
var price = frogPrices[frogType];
var statusText = '';
if (isSelected) {
statusText = ' (SELECTED)';
} else if (isOwned || price === 0) {
statusText = ' (OWNED)';
} else {
statusText = ' - ' + price + ' coins';
}
var frogNameTxt = new Text2(frogNames[frogType] + statusText, {
size: 50,
fill: isSelected ? 0x00ff00 : 0xFFFFFF
});
frogNameTxt.anchor.set(0, 0.5);
frogNameTxt.x = 500;
frogNameTxt.y = yPos;
game.addChild(frogNameTxt);
storeTexts.push(frogNameTxt);
// Store frog type for purchase detection
frogPreview.frogType = frogType;
}
// Create powerup items
var powerupTypes = ['coinMultiplier'];
for (var i = 0; i < powerupTypes.length; i++) {
var powerupType = powerupTypes[i];
var yPos = 1400 + i * 200;
// Create powerup preview (using coin asset)
var powerupPreview = game.addChild(LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: yPos
}));
powerupPreview.tint = 0x00ff00; // Green tint for powerups
storeItems.push(powerupPreview);
// Create name and price text
var price = powerupPrices[powerupType];
var gamesLeft = storage.coinMultiplierGamesLeft || 0;
var statusText = '';
if (powerupType === 'coinMultiplier' && gamesLeft > 0) {
statusText = ' (' + gamesLeft + ' games left)';
} else {
statusText = ' - ' + price + ' coins';
}
var powerupNameTxt = new Text2(powerupNames[powerupType] + statusText, {
size: 50,
fill: powerupType === 'coinMultiplier' && gamesLeft > 0 ? 0x00ff00 : 0xFFFFFF
});
powerupNameTxt.anchor.set(0, 0.5);
powerupNameTxt.x = 500;
powerupNameTxt.y = yPos;
game.addChild(powerupNameTxt);
storeTexts.push(powerupNameTxt);
// Store powerup type for purchase detection
powerupPreview.powerupType = powerupType;
}
// Create back button
var backButton = game.addChild(LK.getAsset('stone', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200
}));
var backTxt = new Text2('BACK', {
size: 80,
fill: 0x000000
});
backTxt.anchor.set(0.5, 0.5);
backTxt.x = 1024;
backTxt.y = 2200;
game.addChild(backTxt);
storeItems.push(backButton);
storeTexts.push(backTxt);
backButton.isBackButton = true;
}
function startGame() {
gameStarted = true;
showingStore = false;
// Initialize coin multiplier from storage
coinMultiplierGamesLeft = storage.coinMultiplierGamesLeft || 0;
if (titleTxt) titleTxt.destroy();
if (playButton) playButton.destroy();
if (storeButton) storeButton.destroy();
// Clean up store items
for (var i = 0; i < storeItems.length; i++) {
storeItems[i].destroy();
}
storeItems = [];
// Clean up store texts
for (var i = 0; i < storeTexts.length; i++) {
storeTexts[i].destroy();
}
storeTexts = [];
// Remove play text by destroying all text children except UI
var gameChildren = game.children.slice();
for (var i = 0; i < gameChildren.length; i++) {
if (gameChildren[i] instanceof Text2) {
gameChildren[i].destroy();
}
}
initializeLevel();
}
// Show start menu instead of initializing level
showStartMenu();
game.down = function (x, y, obj) {
if (!gameStarted && !showingStore) {
// Check if play button was clicked
if (playButton) {
var distance = Math.sqrt(Math.pow(x - playButton.x, 2) + Math.pow(y - playButton.y, 2));
if (distance < 100) {
startGame();
return;
}
}
// Check if store button was clicked
if (storeButton) {
var distance = Math.sqrt(Math.pow(x - storeButton.x, 2) + Math.pow(y - storeButton.y, 2));
if (distance < 100) {
showStore();
return;
}
}
return;
}
if (showingStore) {
// Handle store interactions
for (var i = 0; i < storeItems.length; i++) {
var item = storeItems[i];
var distance = Math.sqrt(Math.pow(x - item.x, 2) + Math.pow(y - item.y, 2));
if (distance < 100) {
if (item.isBackButton) {
showingStore = false;
// Clean up store
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStartMenu();
return;
} else if (item.frogType) {
// Try to buy/select frog
var frogType = item.frogType;
var price = frogPrices[frogType];
var isOwned = storage.ownedFrogs && storage.ownedFrogs[frogType];
var currentCoins = storage.coins || 0;
if (price === 0 || isOwned) {
// Select this frog
storage.selectedFrog = frogType;
currentFrogType = frogType;
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
} else if (currentCoins >= price) {
// Buy frog
storage.coins = currentCoins - price;
if (!storage.ownedFrogs) storage.ownedFrogs = {};
storage.ownedFrogs[frogType] = true;
storage.selectedFrog = frogType;
currentFrogType = frogType;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
}
} else if (item.powerupType) {
// Try to buy powerup
var powerupType = item.powerupType;
var price = powerupPrices[powerupType];
var currentCoins = storage.coins || 0;
if (powerupType === 'coinMultiplier' && currentCoins >= price) {
// Buy coin multiplier for 3 games
storage.coins = currentCoins - price;
storage.coinMultiplierGamesLeft = 3;
coinMultiplierGamesLeft = 3;
// Update coin display
coinsTxt.setText('Coins: ' + storage.coins);
// Refresh store display
showingStore = false;
for (var j = 0; j < storeItems.length; j++) {
storeItems[j].destroy();
}
storeItems = [];
for (var j = 0; j < storeTexts.length; j++) {
storeTexts[j].destroy();
}
storeTexts = [];
showStore();
}
}
break;
}
}
return;
}
if (frog.isJumping && frog.frogType !== 'double') return;
if (frog.isJumping && frog.frogType === 'double' && frog.doubleJumpUsed) return;
var nearestStone = null;
var nearestDistance = Infinity;
for (var i = 0; i < stones.length; i++) {
var stone = stones[i];
var distance = Math.sqrt(Math.pow(x - stone.x, 2) + Math.pow(y - stone.y, 2));
if (distance < 150 && distance < nearestDistance) {
nearestDistance = distance;
nearestStone = stone;
}
}
if (nearestStone && !nearestStone.isOccupied) {
if (frog.isJumping && frog.frogType === 'double') {
frog.doubleJump(nearestStone);
} else {
frog.jumpTo(nearestStone);
}
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Score: ' + LK.getScore());
}
};
game.update = function () {
if (!gameStarted) return;
// Clean up destroyed stones
for (var i = stones.length - 1; i >= 0; i--) {
if (stones[i].destroyed) {
stones.splice(i, 1);
}
}
// Clean up destroyed fish
for (var i = fishes.length - 1; i >= 0; i--) {
if (fishes[i].destroyed) {
fishes.splice(i, 1);
}
}
// Clean up destroyed coins
for (var i = coins.length - 1; i >= 0; i--) {
if (coins[i].destroyed) {
coins.splice(i, 1);
}
}
// Check coin collection
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (frog.intersects(coin) && !coin.collected) {
coin.collect();
coins.splice(i, 1);
}
}
// Update distance traveled
var currentDistance = Math.max(0, Math.floor((startY - frog.y) / 10));
if (currentDistance > maxDistance) {
maxDistance = currentDistance;
storage.maxDistance = maxDistance;
}
distanceTxt.setText('Distance: ' + currentDistance + 'm');
// Spawn fish periodically - more fish at higher levels
fishSpawnTimer++;
var fishSpawnRate = Math.max(30, 120 - level * 8); // Minimum 30 frames between spawns
if (fishSpawnTimer > fishSpawnRate) {
fishSpawnTimer = 0;
// Spawn multiple fish at higher levels
var fishCount = Math.min(3, Math.floor(level / 3) + 1);
for (var f = 0; f < fishCount; f++) {
spawnFish();
}
}
checkWinCondition();
checkGameOver();
};