User prompt
when an invader hits the bottom. teleport the invader back to the top
User prompt
each Bounce the invader gets. the lower it gets
User prompt
each bounce the invader gets the faster the invaders get. and add red invaders that is 2x faster
User prompt
start the percentage by 0% when it hits 100% show the countdown. and when you double tap when the super bar is full you enable the super shot. And when you’re shooting the super shot. a laser appears from your cannon destroying everything in its way except all players
User prompt
if the invaders are shooted make another level. and when the bar is on 100% add a 10 second countdown over your cannon. same with other cannons. and show the percentage of the super bar
User prompt
add huge invaders and make them split when shooted
User prompt
apply the when you shoot an invader you gain a percentage to all of the players. and add invaders so you can shoot them.
User prompt
if you shoot an invader. you gain a percentage. not if you shoot.
User prompt
every thing you shoot. you gain a percentage of a super bar. so when you shoot an invader. a percentage pops up over the super bar and the name of the cannon and every thing you shoot. You gain 1% of your super bar.
User prompt
make the mega bar big and only appear in gameplay
User prompt
name the title “Pixelossed Rush Frenzy 2”
User prompt
undo the title to the current title!
User prompt
make thy ex
User prompt
add a mega bar at the side of the screen and add a super bar over all players.
User prompt
every 20 seconds a power up comes in. Make the power ups random and only these power ups added to this list. 1. Super Filler 2. Healer 3. Shield
User prompt
make all players shoot automatically
User prompt
move all of the cannons to the bottom and only moves left and right
User prompt
make the P1 cannon move when moving the hand and remove the AI movement on P1s cannon
User prompt
move all of the AI-controlled cannons even mines on the bottom and make the AI-controlled move like a player is actually playing the game but it’s not
User prompt
name all of the AI-controlled cannons randomly in gameplay
User prompt
add 12 cannons in the gameplay using the same names that was picked by the AI-controlled Cannons Screen
User prompt
add how many cannons there was in the Online AI-Controlled cannons screen
User prompt
after the loading screen add the AI-controlled cannons also your cannon and add the names that the name picking picked over the ai controlled cannons.
User prompt
add a loading bar which fills up randomly so like it fills up a sertan percent randomly
User prompt
so add a screen saying “Loading...” after the 10 second countdown.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// --- Mega Bar UI (vertical, right side) ---
// Only create and add the mega bar when gameplay starts (not on menu/loading)
var megaBarBg = null;
var megaBarFill = null;
function createMegaBar() {
// Remove if already present
if (megaBarBg && megaBarBg.parent) megaBarBg.parent.removeChild(megaBarBg);
if (megaBarFill && megaBarFill.parent) megaBarFill.parent.removeChild(megaBarFill);
// Make the bar much bigger
megaBarBg = LK.getAsset('box', {
width: 180,
height: 1800,
color: 0x333366,
anchorX: 1,
anchorY: 0
});
megaBarBg.x = 2048 - 40;
megaBarBg.y = 150;
game.addChild(megaBarBg);
megaBarFill = LK.getAsset('box', {
width: 144,
height: 0,
color: 0x66ccff,
anchorX: 1,
anchorY: 1
});
megaBarFill.x = 2048 - 40 + 18;
megaBarFill.y = 150 + 1800 - 18;
game.addChild(megaBarFill);
setMegaBarFill(0);
}
// Helper to set mega bar fill (0 to 1)
function setMegaBarFill(percent) {
if (!megaBarFill) return;
percent = Math.max(0, Math.min(1, percent));
megaBarFill.height = Math.floor(1764 * percent);
megaBarFill.y = 150 + 1800 - 18 - megaBarFill.height;
}
function removeMegaBar() {
if (megaBarBg && megaBarBg.parent) megaBarBg.parent.removeChild(megaBarBg);
if (megaBarFill && megaBarFill.parent) megaBarFill.parent.removeChild(megaBarFill);
megaBarBg = null;
megaBarFill = null;
}
// --- AI and Player Cannon Movement Logic ---
// Import tween plugin for animations
game.setBackgroundColor(0x000000); // Ensure black background
// Create the text, initially invisible
var madeByText = new Text2("Made By", {
size: 90,
fill: 0xFFFFFF
});
madeByText.anchor.set(0.5, 1);
madeByText.alpha = 0;
madeByText.x = 2048 / 2;
madeByText.y = 2732 / 2 - 120;
game.addChild(madeByText);
var studioText = new Text2("Pixelated Studios,", {
size: 180,
fill: 0xFFFFFF
});
studioText.anchor.set(0.5, 0.5);
studioText.alpha = 0;
studioText.x = 2048 / 2;
studioText.y = 2732 / 2;
game.addChild(studioText);
// --- SKIP BUTTON LOGIC ---
var skipBtn = new Text2("Skip", {
size: 80,
fill: 0xffffff,
background: 0x222222,
padding: 40,
borderRadius: 30
});
skipBtn.anchor.set(1, 0);
skipBtn.x = 2048 - 60;
skipBtn.y = 60;
skipBtn.alpha = 0.85;
game.addChild(skipBtn);
var skipIntro = false;
var skipTimeouts = [];
function showTitleScreen() {
// Remove skip button if present
if (skipBtn && skipBtn.parent) skipBtn.parent.removeChild(skipBtn);
// Remove all intro texts if present
if (madeByText && madeByText.parent) madeByText.parent.removeChild(madeByText);
if (studioText && studioText.parent) studioText.parent.removeChild(studioText);
if (geminiMadeByText && geminiMadeByText.parent) geminiMadeByText.parent.removeChild(geminiMadeByText);
if (geminiText && geminiText.parent) geminiText.parent.removeChild(geminiText);
if (andText && andText.parent) andText.parent.removeChild(andText);
if (upitText && upitText.parent) upitText.parent.removeChild(upitText);
// Remove mega bar if present (hide on menu)
removeMegaBar();
// Create the title text
var titleText = new Text2("Pixelossed Rush Frenzy 2", {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 2;
game.addChild(titleText);
// Create Multi-Player and Single-Player buttons just below the title, larger and more visible
var buttonWidth = 900;
var buttonHeight = 180;
var buttonSpacing = 60;
// Position buttons just below the title
var titleBottomY = titleText.y + titleText.height / 2 + 80;
var multiPlayerBtn = new Text2("Multi-Player", {
size: 110,
fill: 0x222222,
background: 0xFFFFFF,
padding: 60,
borderRadius: 40
});
multiPlayerBtn.anchor.set(0.5, 0.5);
multiPlayerBtn.x = 2048 / 2;
multiPlayerBtn.y = titleBottomY + buttonHeight / 2;
var singlePlayerBtn = new Text2("Single-Player", {
size: 110,
fill: 0x222222,
background: 0xFFFFFF,
padding: 60,
borderRadius: 40
});
singlePlayerBtn.anchor.set(0.5, 0.5);
singlePlayerBtn.x = 2048 / 2;
singlePlayerBtn.y = multiPlayerBtn.y + buttonHeight + buttonSpacing;
game.addChild(multiPlayerBtn);
game.addChild(singlePlayerBtn);
// Add .down event handlers to make buttons press-able
multiPlayerBtn.down = function (x, y, obj) {
LK.effects.flashObject(multiPlayerBtn, 0x00ff00, 400);
// Hide the main menu buttons and title
titleText.visible = false;
multiPlayerBtn.visible = false;
singlePlayerBtn.visible = false;
// Show the Multi-Player mode selection screen
// Create AI-Controlled Cannons button
var aiCannonsBtn = new Text2("AI-Controlled Cannons", {
size: 90,
fill: 0x222222,
background: 0xFFFFFF,
padding: 50,
borderRadius: 40
});
aiCannonsBtn.anchor.set(0.5, 0.5);
aiCannonsBtn.x = 2048 / 2;
aiCannonsBtn.y = 2732 / 2 - 120;
// Create Online AI controlled Cannons button
var onlineAICannonsBtn = new Text2("Online AI controlled Cannons", {
size: 90,
fill: 0x222222,
background: 0xFFFFFF,
padding: 50,
borderRadius: 40
});
onlineAICannonsBtn.anchor.set(0.5, 0.5);
onlineAICannonsBtn.x = 2048 / 2;
onlineAICannonsBtn.y = 2732 / 2 + 120;
game.addChild(aiCannonsBtn);
game.addChild(onlineAICannonsBtn);
// Add simple flash feedback for these buttons (actual logic can be added later)
aiCannonsBtn.down = function (x, y, obj) {
LK.effects.flashObject(aiCannonsBtn, 0x00ff00, 400);
// Add logic for AI-Controlled Cannons mode here
};
onlineAICannonsBtn.down = function (x, y, obj) {
LK.effects.flashObject(onlineAICannonsBtn, 0x00ff00, 400);
// Hide the mode selection buttons
aiCannonsBtn.visible = false;
onlineAICannonsBtn.visible = false;
// --- Setup for 3 lines, 4 spaces per line ---
var lines = [];
var spacesPerLine = 4;
var totalLines = 3;
var spaceWidth = 260;
var spaceHeight = 220;
var spaceSpacingX = 80;
var spaceSpacingY = 80;
var startY = 2732 / 2 - (totalLines - 1) * (spaceHeight + spaceSpacingY) / 2 - 100;
var startX = 2048 / 2 - (spacesPerLine - 1) * (spaceWidth + spaceSpacingX) / 2;
// Store references for later if needed
var onlineCannonSpaces = [];
for (var line = 0; line < totalLines; ++line) {
var y = startY + line * (spaceHeight + spaceSpacingY);
for (var col = 0; col < spacesPerLine; ++col) {
var x = startX + col * (spaceWidth + spaceSpacingX);
// Create a container for each space
var spaceContainer = new Container();
spaceContainer.x = x;
spaceContainer.y = y;
// Draw background for the first space of the first line (P1)
if (line === 0 && col === 0) {
var bg = LK.getAsset('box', {
width: spaceWidth,
height: spaceHeight,
color: 0xff2222,
anchorX: 0.5,
anchorY: 0.5
});
spaceContainer.addChild(bg);
} else {
// Draw a neutral background for other spaces
var bg = LK.getAsset('box', {
width: spaceWidth,
height: spaceHeight,
color: 0xeeeeee,
anchorX: 0.5,
anchorY: 0.5
});
spaceContainer.addChild(bg);
}
// Add text below the space (except for first line)
var labelText = "";
if (line === 0) {
// First line: only show P1 under first space, others show "looking for players..."
if (col === 0) {
labelText = "P1";
} else {
labelText = "looking for players...";
}
} else {
// All other lines: show "looking for players..." under all spaces
labelText = "looking for players...";
}
// Only add text if not the first line's spaces (per instructions)
if (!(line === 0)) {
var label = new Text2(labelText, {
size: 48,
fill: 0x333333
});
label.anchor.set(0.5, 0);
label.x = 0;
label.y = spaceHeight / 2 + 10;
spaceContainer.addChild(label);
spaceContainer._label = label; // Store reference for later
} else if (col !== 0) {
// For first line, only add text to spaces 2,3,4
var label = new Text2(labelText, {
size: 48,
fill: 0x333333
});
label.anchor.set(0.5, 0);
label.x = 0;
label.y = spaceHeight / 2 + 10;
spaceContainer.addChild(label);
spaceContainer._label = label; // Store reference for later
} else if (col === 0) {
// For first space, add "P1" below
var p1Label = new Text2("P1", {
size: 48,
fill: 0xffffff
});
p1Label.anchor.set(0.5, 0);
p1Label.x = 0;
p1Label.y = spaceHeight / 2 + 10;
spaceContainer.addChild(p1Label);
spaceContainer._label = null; // No random name for P1
}
// Center anchor for the container
spaceContainer.pivot.x = 0;
spaceContainer.pivot.y = 0;
game.addChild(spaceContainer);
onlineCannonSpaces.push(spaceContainer);
}
}
// After 5 seconds, show random names under the spaces (except P1)
LK.setTimeout(function () {
// List of random names to pick from
var randomNames = ["PixelHero", "GeminiBot", "UpitAI", "CannonKing", "BlasterX", "NovaRush", "Firestorm", "Zapster", "RedRocket", "BlueBolt", "GreenGunner", "ShadowAI", "LaserLynx", "TurboTiger", "CyberCannon", "Vortex"];
// Shuffle the names array
for (var i = randomNames.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = randomNames[i];
randomNames[i] = randomNames[j];
randomNames[j] = temp;
}
var nameIdx = 0;
for (var i = 0; i < onlineCannonSpaces.length; ++i) {
var space = onlineCannonSpaces[i];
// Only update if this space has a label (not P1)
if (space._label) {
// Pick a random name, cycle if we run out
var name = randomNames[nameIdx % randomNames.length];
space._label.setText(name);
// Make the name more visible and white
if (space._label.style) {
space._label.style.fill = 0xffffff;
space._label.style.size = 64;
}
nameIdx++;
} else if (i === 0) {
// Make P1 label more visible and white
for (var c = 0; c < space.children.length; ++c) {
var child = space.children[c];
if (child && child.setText && child.text === "P1" && child.style) {
child.style.fill = 0xffffff;
child.style.size = 64;
}
}
}
}
// --- 10 second countdown timer in bottom right ---
var countdownTime = 10;
var countdownText = new Text2(countdownTime + "", {
size: 120,
fill: 0xffffff,
background: 0x222222,
padding: 30,
borderRadius: 40
});
countdownText.anchor.set(1, 1);
// Place in bottom right, with margin
countdownText.x = 2048 - 60;
countdownText.y = 2732 - 60;
game.addChild(countdownText);
var countdownInterval = LK.setInterval(function () {
countdownTime--;
if (countdownTime >= 0) {
countdownText.setText(countdownTime + "");
}
if (countdownTime <= 0) {
LK.clearInterval(countdownInterval);
// Optionally, you can hide or remove the timer here
// countdownText.visible = false;
// After countdown, show "Loading…" screen
// Hide all spaces and countdown
for (var i = 0; i < onlineCannonSpaces.length; ++i) {
if (onlineCannonSpaces[i] && onlineCannonSpaces[i].parent) {
onlineCannonSpaces[i].parent.removeChild(onlineCannonSpaces[i]);
}
}
if (countdownText && countdownText.parent) {
countdownText.parent.removeChild(countdownText);
}
// Show "Loading…" centered
var loadingText = new Text2("Loading…", {
size: 180,
fill: 0xffffff,
background: 0x222222,
padding: 80,
borderRadius: 60
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 2048 / 2;
loadingText.y = 2732 / 2 - 120;
game.addChild(loadingText);
// --- Loading Bar Logic ---
var barWidth = 900;
var barHeight = 80;
var barBg = LK.getAsset('box', {
width: barWidth,
height: barHeight,
color: 0x444444,
anchorX: 0.5,
anchorY: 0.5
});
barBg.x = 2048 / 2;
barBg.y = 2732 / 2 + 80;
game.addChild(barBg);
var barFill = LK.getAsset('box', {
width: 1,
height: barHeight - 16,
color: 0xffffff,
anchorX: 0,
anchorY: 0.5
});
barFill.x = 2048 / 2 - barWidth / 2 + 8;
barFill.y = 2732 / 2 + 80;
game.addChild(barFill);
var loadingPercent = 0;
var loadingBarInterval = LK.setInterval(function () {
// Randomly increase percent by 3-18% per tick, but never above 100
var increment = Math.floor(Math.random() * 16) + 3;
loadingPercent += increment;
if (loadingPercent > 100) loadingPercent = 100;
// Animate bar width
barFill.width = Math.floor((barWidth - 16) * (loadingPercent / 100));
// Optionally, show percent text above bar
if (!barFill._percentText) {
var percentText = new Text2("0%", {
size: 60,
fill: 0xffffff,
background: 0x222222,
padding: 18,
borderRadius: 30
});
percentText.anchor.set(0.5, 1);
percentText.x = 2048 / 2;
percentText.y = barBg.y - barHeight / 2 - 10;
game.addChild(percentText);
barFill._percentText = percentText;
}
barFill._percentText.setText(loadingPercent + "%");
// If full, stop interval (simulate loading complete)
if (loadingPercent >= 100) {
LK.clearInterval(loadingBarInterval);
// Optionally, fade out loading bar and text after a short delay
LK.setTimeout(function () {
if (loadingText && loadingText.parent) loadingText.parent.removeChild(loadingText);
if (barBg && barBg.parent) barBg.parent.removeChild(barBg);
if (barFill && barFill.parent) barFill.parent.removeChild(barFill);
if (barFill._percentText && barFill._percentText.parent) barFill._percentText.parent.removeChild(barFill._percentText);
// --- After loading, show AI-controlled cannons and player cannon with names ---
// Show mega bar now that gameplay is starting
createMegaBar();
// We'll use the same random names as before, and show them above the AI cannons
// Get the names that were picked for the AI cannons (from previous randomization)
var aiCannonNames = [];
for (var i = 0; i < onlineCannonSpaces.length; ++i) {
var space = onlineCannonSpaces[i];
if (space && space._label && space._label.text) {
aiCannonNames.push(space._label.text);
}
}
// The first is always "P1" (player), the rest are AI names
// We'll show 12 cannons in 2 rows of 6, player on the top left, 11 AI to the right and below
var cannonCount = 12;
var cannonsPerRow = 6;
var cannonSpacingX = 320;
var cannonSpacingY = 340;
// Move all cannons to the bottom and restrict movement to left/right only
var cannonYStart = 2732 - 220; // Place all cannons near the bottom of the screen
var cannonXStart = 2048 / 2 - (cannonsPerRow - 1) * cannonSpacingX / 2;
// Store references for later if needed
var cannonSprites = [];
var cannonNameLabels = [];
var cannonSuperBars = []; // Super bar UI for each cannon
// Prepare a list of random names for AI cannons (excluding P1)
var aiCannonRandomNames = ["PixelHero", "GeminiBot", "UpitAI", "CannonKing", "BlasterX", "NovaRush", "Firestorm", "Zapster", "RedRocket", "BlueBolt", "GreenGunner", "ShadowAI", "LaserLynx", "TurboTiger", "CyberCannon", "Vortex", "IronBlast", "MegaShot", "ThunderCore", "BlastMaster", "Rocketron", "SteelBarrel", "PyroPulse", "BoltBlaze"];
// Shuffle the names array
for (var i = aiCannonRandomNames.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = aiCannonRandomNames[i];
aiCannonRandomNames[i] = aiCannonRandomNames[j];
aiCannonRandomNames[j] = temp;
}
var aiCannonNameIdx = 0;
for (var i = 0; i < cannonCount; ++i) {
var row = Math.floor(i / cannonsPerRow);
var col = i % cannonsPerRow;
var x = cannonXStart + col * cannonSpacingX;
var y = cannonYStart + row * cannonSpacingY;
// Create a cannon (use 'box' asset for now)
var cannonColor = i === 0 ? 0xff2222 : 0xeeeeee;
var cannon = LK.getAsset('box', {
width: 180,
height: 180,
color: cannonColor,
anchorX: 0.5,
anchorY: 1
});
cannon.x = x;
cannon.y = y;
game.addChild(cannon);
cannonSprites.push(cannon);
// Add the name above the cannon
var nameText = "";
if (i === 0) {
nameText = "P1";
} else {
// Assign a random name from the shuffled list, cycling if needed
nameText = aiCannonRandomNames[aiCannonNameIdx % aiCannonRandomNames.length];
aiCannonNameIdx++;
}
var nameLabel = new Text2(nameText, {
size: 80,
fill: 0xffffff,
background: 0x222222,
padding: 24,
borderRadius: 30
});
nameLabel.anchor.set(0.5, 1);
nameLabel.x = x;
nameLabel.y = y - 200;
game.addChild(nameLabel);
cannonNameLabels.push(nameLabel);
// --- Super Bar (above each cannon) ---
var superBarBg = LK.getAsset('box', {
width: 160,
height: 24,
color: 0x222222,
anchorX: 0.5,
anchorY: 1
});
superBarBg.x = x;
superBarBg.y = y - 220;
game.addChild(superBarBg);
var superBarFill = LK.getAsset('box', {
width: 152,
height: 16,
color: 0x66ff66,
anchorX: 0.5,
anchorY: 1
});
superBarFill.x = x;
superBarFill.y = y - 220 + 4;
game.addChild(superBarFill);
// Helper to set super bar fill (0 to 1)
superBarFill._setFill = function (percent) {
percent = Math.max(0, Math.min(1, percent));
superBarFill.width = Math.floor(152 * percent);
};
superBarFill._setFill(1); // Start full
cannonSuperBars.push({
bg: superBarBg,
fill: superBarFill
});
}
// Show the number of cannons below the rows, centered
var cannonCountText = new Text2("Cannons: " + cannonCount, {
size: 80,
fill: 0xffffff,
background: 0x222222,
padding: 24,
borderRadius: 30
});
cannonCountText.anchor.set(0.5, 0);
cannonCountText.x = 2048 / 2;
cannonCountText.y = cannonYStart + 2 * cannonSpacingY + 40;
game.addChild(cannonCountText);
// --- AI and Player Cannon Movement Logic ---
// We'll move all cannons (including player) on the bottom row, and make AI move like a player
// Only bottom row (row 1, i=6..11) is movable, but for this task, move all cannons (including player) on the bottom
// Store cannon state for movement
var cannonStates = [];
for (var i = 0; i < cannonSprites.length; ++i) {
// All cannons start with a random direction and speed
var isPlayer = i === 0;
cannonStates.push({
vx: (Math.random() * 2 - 1) * 6,
// -6 to 6 px/frame
vy: 0,
targetX: cannonSprites[i].x,
targetY: cannonSprites[i].y,
moveCooldown: 0,
isPlayer: isPlayer,
aiMoveTimer: 0,
aiTargetX: cannonSprites[i].x,
aiTargetY: cannonSprites[i].y,
shootCooldown: Math.floor(Math.random() * 30) + 30 // randomize initial shoot cooldown (0.5s-1s)
});
}
// --- Automatic Shooting for All Cannons ---
// We'll create a bullet asset and manage bullets in an array
var bulletAssetColor = 0x44aaff;
var bulletWidth = 32;
var bulletHeight = 80;
var bulletSpeed = -32; // negative Y, shoots upward
var bullets = [];
// --- Super Bar Percentage Tracking ---
var superBarPercents = [];
for (var i = 0; i < 12; ++i) {
superBarPercents.push(1); // Start full (100%)
}
// Helper to create a bullet at (x, y)
function createBullet(x, y, color) {
var bullet = LK.getAsset('box', {
width: bulletWidth,
height: bulletHeight,
color: color || bulletAssetColor,
anchorX: 0.5,
anchorY: 1
});
bullet.x = x;
bullet.y = y - 90; // start just above the cannon
bullet._vy = bulletSpeed;
// Track which cannon shot this bullet (for super bar)
bullet._shooterIdx = null;
return bullet;
}
// Helper to show a +1% popup above a super bar
function showSuperBarPopup(idx, amount) {
if (!cannonSuperBars[idx]) return;
var bar = cannonSuperBars[idx].fill;
var cannonLabel = cannonNameLabels[idx];
var popup = new Text2("+" + amount + "%", {
size: 54,
fill: 0xffff00,
background: 0x222222,
padding: 12,
borderRadius: 18
});
popup.anchor.set(0.5, 1);
popup.x = bar.x;
popup.y = bar.y - 16;
game.addChild(popup);
// Show the name of the cannon above the popup
var namePopup = null;
if (cannonLabel && cannonLabel.text) {
namePopup = new Text2(cannonLabel.text, {
size: 38,
fill: 0xffffff,
background: 0x222222,
padding: 8,
borderRadius: 12
});
namePopup.anchor.set(0.5, 1);
namePopup.x = bar.x;
namePopup.y = popup.y - 48;
game.addChild(namePopup);
}
// Animate popup up and fade out
tween(popup, {
y: popup.y - 60,
alpha: 0
}, {
duration: 900,
easing: tween.easeOut,
onComplete: function onComplete() {
if (popup.parent) popup.parent.removeChild(popup);
}
});
if (namePopup) {
tween(namePopup, {
y: namePopup.y - 60,
alpha: 0
}, {
duration: 900,
easing: tween.easeOut,
onComplete: function onComplete() {
if (namePopup.parent) namePopup.parent.removeChild(namePopup);
}
});
}
}
// Helper: clamp value
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Movement bounds (keep cannons inside visible area)
var minX = 120;
var maxX = 2048 - 120;
var minY = cannonYStart - 40;
var maxY = cannonYStart + cannonSpacingY + 40;
// Simulate "player" movement (bottom left cannon, i=0) as AI for now
// All other cannons are AI
// Animate movement every frame
game.update = function () {
// --- Cannon Movement and Name Label Update ---
for (var i = 0; i < cannonSprites.length; ++i) {
var cannon = cannonSprites[i];
var state = cannonStates[i];
// --- Super Bar UI follows cannon ---
if (cannonSuperBars[i]) {
cannonSuperBars[i].bg.x = cannon.x;
cannonSuperBars[i].bg.y = cannon.y - 220;
cannonSuperBars[i].fill.x = cannon.x;
cannonSuperBars[i].fill.y = cannon.y - 220 + 4;
}
// Animate super bar fill and show percentage
if (cannonSuperBars[i]) {
// Set fill to actual value
var percent = superBarPercents[i];
cannonSuperBars[i].fill._setFill(percent);
// Show percentage text above the bar
if (!cannonSuperBars[i]._percentText) {
var pctText = new Text2("100%", {
size: 38,
fill: 0xffffff,
background: 0x222222,
padding: 8,
borderRadius: 12
});
pctText.anchor.set(0.5, 1);
pctText.x = cannonSuperBars[i].fill.x;
pctText.y = cannonSuperBars[i].fill.y - 18;
game.addChild(pctText);
cannonSuperBars[i]._percentText = pctText;
}
// Update percent text and position
if (cannonSuperBars[i]._percentText) {
cannonSuperBars[i]._percentText.setText(Math.floor(percent * 100) + "%");
cannonSuperBars[i]._percentText.x = cannonSuperBars[i].fill.x;
cannonSuperBars[i]._percentText.y = cannonSuperBars[i].fill.y - 18;
}
// --- Show 10s countdown over cannon if super bar is 100% and not already counting down ---
if (!cannonSuperBars[i]._superCountdown && percent >= 1) {
// Create countdown text
var cdText = new Text2("10", {
size: 90,
fill: 0xffee00,
background: 0x222222,
padding: 18,
borderRadius: 24
});
cdText.anchor.set(0.5, 0.5);
cdText.x = cannonSprites[i].x;
cdText.y = cannonSprites[i].y - 120;
game.addChild(cdText);
cannonSuperBars[i]._superCountdown = {
text: cdText,
time: 10 * 60 // 10 seconds at 60fps
};
}
// If counting down, update timer and text
if (cannonSuperBars[i]._superCountdown) {
var sc = cannonSuperBars[i]._superCountdown;
sc.text.x = cannonSprites[i].x;
sc.text.y = cannonSprites[i].y - 120;
sc.time--;
var secondsLeft = Math.ceil(sc.time / 60);
sc.text.setText(secondsLeft + "");
if (sc.time <= 0) {
// Countdown finished, remove text and reset super bar
if (sc.text.parent) sc.text.parent.removeChild(sc.text);
cannonSuperBars[i]._superCountdown = null;
superBarPercents[i] = 0;
cannonSuperBars[i].fill._setFill(0);
if (cannonSuperBars[i]._percentText) cannonSuperBars[i]._percentText.setText("0%");
}
}
}
if (i === 0) {
// P1 cannon: do not move by AI, position is set by hand/touch events
// Just update the name label to follow the cannon
if (cannonNameLabels[i]) {
cannonNameLabels[i].x = cannon.x;
cannonNameLabels[i].y = cannon.y - 200;
}
} else {
// AI logic: every 1.2-2.5s, pick a new random target X position (Y is fixed)
if (state.aiMoveTimer <= 0) {
state.aiTargetX = clamp(cannon.x + (Math.random() * 2 - 1) * 400, minX, maxX);
state.aiTargetY = cannonYStart; // Always at the bottom
state.aiMoveTimer = 72 + Math.floor(Math.random() * 60); // 1.2s to 2.2s at 60fps
} else {
state.aiMoveTimer--;
}
// Move towards target (only X axis, Y is fixed)
var dx = state.aiTargetX - cannon.x;
var dist = Math.abs(dx);
var speed = 10 + Math.random() * 2; // px per frame, a bit jittery
if (dist > 2) {
cannon.x += dx / dist * Math.min(speed, dist);
cannon.y = cannonYStart; // Always at the bottom
// Also move the name label
if (cannonNameLabels[i]) {
cannonNameLabels[i].x = cannon.x;
cannonNameLabels[i].y = cannon.y - 200;
}
} else {
// Snap to target if close
cannon.x = state.aiTargetX;
cannon.y = cannonYStart; // Always at the bottom
if (cannonNameLabels[i]) {
cannonNameLabels[i].x = cannon.x;
cannonNameLabels[i].y = cannon.y - 200;
}
}
}
// --- Automatic Shooting ---
if (state.shootCooldown > 0) {
state.shootCooldown--;
} else {
// Shoot a bullet
var bulletColor = i === 0 ? 0xff2222 : bulletAssetColor;
var bullet = createBullet(cannon.x, cannon.y, bulletColor);
bullet._shooterIdx = i;
game.addChild(bullet);
bullets.push(bullet);
// Reset shoot cooldown (randomize a bit for AI, fixed for player)
if (i === 0) {
state.shootCooldown = 30; // P1: shoot every 0.5s
} else {
state.shootCooldown = 24 + Math.floor(Math.random() * 24); // AI: 0.4s-0.8s
}
}
}
// --- Animate mega bar fill (demo pulse) ---
if (megaBarFill) {
var megaPercent = 0.5 + 0.5 * Math.sin(LK.ticks / 120 * Math.PI * 2);
setMegaBarFill(megaPercent);
}
// --- Bullet Movement and Cleanup ---
for (var b = bullets.length - 1; b >= 0; --b) {
var bullet = bullets[b];
bullet.y += bullet._vy;
// --- Add Invaders (move and draw) ---
// We'll create invaders if not already present
if (!game._invadersInitialized) {
game._invaders = [];
var invaderRows = 3;
var invaderCols = 8;
var invaderSpacingX = 180;
var invaderSpacingY = 160;
var invaderStartX = 2048 / 2 - (invaderCols - 1) * invaderSpacingX / 2;
var invaderStartY = 320;
for (var ir = 0; ir < invaderRows; ++ir) {
for (var ic = 0; ic < invaderCols; ++ic) {
// Place huge invaders in the center columns of the first row
if (ir === 0 && (ic === 3 || ic === 4)) {
var hugeInv = createHugeInvader(invaderStartX + ic * invaderSpacingX, invaderStartY + ir * invaderSpacingY, 3, 0xffa500);
game.addChild(hugeInv);
game._invaders.push(hugeInv);
} else {
var invader = LK.getAsset('box', {
width: 120,
height: 80,
color: 0x33ff99,
anchorX: 0.5,
anchorY: 0.5
});
invader.x = invaderStartX + ic * invaderSpacingX;
invader.y = invaderStartY + ir * invaderSpacingY;
invader._vx = 2 + Math.random() * 2;
invader._vy = 0;
invader._dir = Math.random() > 0.5 ? 1 : -1;
invader._row = ir;
invader._col = ic;
invader._alive = true;
invader._lastX = invader.x;
invader._lastY = invader.y;
invader._isHuge = false;
game.addChild(invader);
game._invaders.push(invader);
}
}
}
game._invadersInitialized = true;
}
// Move invaders (simple left-right bounce)
if (game._invaders) {
for (var ii = 0; ii < game._invaders.length; ++ii) {
var inv = game._invaders[ii];
if (!inv._alive) continue;
inv._lastX = inv.x;
inv._lastY = inv.y;
inv.x += inv._vx * inv._dir;
// Bounce at screen edges
if (inv.x < 120) {
inv.x = 120;
inv._dir = 1;
}
if (inv.x > 2048 - 120) {
inv.x = 2048 - 120;
inv._dir = -1;
}
}
}
// --- Check collision with invaders (all invaders) ---
var hitInvader = -1;
if (game._invaders) {
for (var ci = 0; ci < game._invaders.length; ++ci) {
var invader = game._invaders[ci];
if (!invader._alive) continue;
// Simple bounding box collision
if (bullet.x + bullet.width / 2 > invader.x - invader.width / 2 && bullet.x - bullet.width / 2 < invader.x + invader.width / 2 && bullet.y < invader.y + invader.height / 2 && bullet.y + bullet.height > invader.y - invader.height / 2) {
hitInvader = ci;
break;
}
}
}
if (hitInvader !== -1 && bullet._shooterIdx !== null) {
// Only increment super bar if shooter exists and hit an invader
var shooterIdx = bullet._shooterIdx;
superBarPercents[shooterIdx] = clamp(superBarPercents[shooterIdx] + 0.01, 0, 1);
if (cannonSuperBars[shooterIdx] && cannonSuperBars[shooterIdx].fill && typeof cannonSuperBars[shooterIdx].fill._setFill === "function") {
cannonSuperBars[shooterIdx].fill._setFill(superBarPercents[shooterIdx]);
}
showSuperBarPopup(shooterIdx, 1);
// Remove bullet
if (bullet.parent) bullet.parent.removeChild(bullet);
bullets.splice(b, 1);
// Remove invader
var inv = game._invaders[hitInvader];
if (inv && inv.parent) {
// If it's a huge invader, split it!
if (inv._isHuge && inv._size > 1) {
// Split into two smaller invaders of size-1
var splitSize = inv._size - 1;
var offset = 80 * splitSize;
for (var s = -1; s <= 1; s += 2) {
var splitInv = createHugeInvader(inv.x + s * offset, inv.y, splitSize, splitSize === 2 ? 0xffd700 : 0xff69b4 // gold for big, pink for small
);
// Give them a little horizontal velocity away from center
splitInv._vx = 2.5 * s;
splitInv._dir = s;
game.addChild(splitInv);
game._invaders.push(splitInv);
}
}
inv._alive = false;
inv.parent.removeChild(inv);
}
// --- Check if all invaders are destroyed, then start next level ---
var allDead = true;
for (var checki = 0; checki < game._invaders.length; ++checki) {
if (game._invaders[checki]._alive) {
allDead = false;
break;
}
}
if (allDead) {
// Remove any remaining invader objects from stage
for (var remi = 0; remi < game._invaders.length; ++remi) {
if (game._invaders[remi].parent) game._invaders[remi].parent.removeChild(game._invaders[remi]);
}
// Next level: re-initialize invaders, increase difficulty if desired
game._invaders = [];
game._invadersInitialized = false;
// Optionally, you can increase invaderRows, invaderCols, or speed for more challenge
}
continue;
}
// Remove bullet if off screen
if (bullet.y + bullet.height < 0) {
if (bullet.parent) bullet.parent.removeChild(bullet);
bullets.splice(b, 1);
}
}
// --- Power-Up Logic ---
// Power-up types
var powerUpTypes = [{
name: "Super Filler",
color: 0xffe066
}, {
name: "Healer",
color: 0x66ffb3
}, {
name: "Shield",
color: 0x66aaff
}];
var powerUps = [];
var powerUpWidth = 120;
var powerUpHeight = 120;
var powerUpSpeed = 8; // px per frame, falls down
// Helper to create a power-up at (x, y) with a type index
function createPowerUp(x, y, typeIdx) {
var type = powerUpTypes[typeIdx];
var pu = LK.getAsset('box', {
width: powerUpWidth,
height: powerUpHeight,
color: type.color,
anchorX: 0.5,
anchorY: 0.5
});
pu.x = x;
pu.y = y;
pu._vy = powerUpSpeed;
pu._typeIdx = typeIdx;
// Add label
var label = new Text2(type.name, {
size: 38,
fill: 0x222222,
background: 0xffffff,
padding: 10,
borderRadius: 18
});
label.anchor.set(0.5, 0.5);
label.x = 0;
label.y = 0;
pu.addChild(label);
return pu;
}
// Power-up spawn timer
var powerUpInterval = 1200; // 20 seconds at 60fps
var powerUpTimer = powerUpInterval;
game.updatePowerUps = function () {
// Spawn logic
powerUpTimer--;
if (powerUpTimer <= 0) {
// Pick random type
var idx = Math.floor(Math.random() * powerUpTypes.length);
// Spawn at random X along the top, not too close to edges
var px = Math.floor(Math.random() * (maxX - minX - 200)) + minX + 100;
var py = -powerUpHeight / 2;
var pu = createPowerUp(px, py, idx);
game.addChild(pu);
powerUps.push(pu);
powerUpTimer = powerUpInterval;
}
// Move power-ups
for (var p = powerUps.length - 1; p >= 0; --p) {
var pu = powerUps[p];
pu.y += pu._vy;
// Remove if off screen
if (pu.y - powerUpHeight / 2 > 2732) {
if (pu.parent) pu.parent.removeChild(pu);
powerUps.splice(p, 1);
continue;
}
// TODO: Add collision logic with cannons if needed
}
};
// Call power-up update each frame
game.updatePowerUps();
};
// --- P1 Cannon Hand/Touch Movement Logic ---
var p1Dragging = false;
game.down = function (x, y, obj) {
// Only allow drag if touch/click is within P1 cannon bounds
var p1 = cannonSprites[0];
// Use bounding box for hit test
var halfW = p1.width / 2;
var halfH = p1.height;
if (x >= p1.x - halfW && x <= p1.x + halfW && y >= p1.y - halfH && y <= p1.y) {
p1Dragging = true;
}
};
game.move = function (x, y, obj) {
if (p1Dragging) {
// Clamp movement to allowed bounds, only X axis, Y is fixed at bottom
var p1 = cannonSprites[0];
var halfW = p1.width / 2;
var newX = clamp(x, minX, maxX);
p1.x = newX;
p1.y = cannonYStart; // Always at the bottom
// Move the name label as well
if (cannonNameLabels[0]) {
cannonNameLabels[0].x = p1.x;
cannonNameLabels[0].y = p1.y - 200;
}
}
};
game.up = function (x, y, obj) {
p1Dragging = false;
};
}, 800);
}
}, 350);
}
}, 1000);
}, 5000);
};
};
singlePlayerBtn.down = function (x, y, obj) {
// You can add your Single-Player logic here
LK.effects.flashObject(singlePlayerBtn, 0x00ff00, 400);
// For now, just flash the button to show it was pressed
};
}
// Show title immediately
studioText.alpha = 1;
madeByText.alpha = 1;
// Helper to clear all intro timeouts if skipping
function clearIntroTimeouts() {
for (var i = 0; i < skipTimeouts.length; ++i) {
LK.clearTimeout(skipTimeouts[i]);
}
skipTimeouts = [];
}
// Add skip button handler
skipBtn.down = function (x, y, obj) {
skipIntro = true;
clearIntroTimeouts();
showTitleScreen();
};
// Stay for 2 seconds, then hide instantly
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
studioText.alpha = 0;
madeByText.alpha = 0;
}, 2000));
// After Pixelated Studios, show Gemini Games
var geminiMadeByText = new Text2("Made By", {
size: 90,
fill: 0xFFFFFF
});
geminiMadeByText.anchor.set(0.5, 1);
geminiMadeByText.alpha = 0;
geminiMadeByText.x = 2048 / 2;
geminiMadeByText.y = 2732 / 2 - 120;
game.addChild(geminiMadeByText);
var geminiText = new Text2("Gemini Games,", {
size: 180,
fill: 0xFFFFFF
});
geminiText.anchor.set(0.5, 0.5);
geminiText.alpha = 0;
geminiText.x = 2048 / 2;
geminiText.y = 2732 / 2;
game.addChild(geminiText);
// 3 seconds after Pixelated Studios fades out (4+600+2000+600+3000 = 10200ms)
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
tween(geminiText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
tween(geminiMadeByText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
tween(geminiText, {
alpha: 0
}, {
duration: 600,
easing: tween.easeIn
});
tween(geminiMadeByText, {
alpha: 0
}, {
duration: 600,
easing: tween.easeIn
});
}, 2000));
}, 10200));
// After Gemini Games, show Upit Studios
var andText = new Text2("And", {
size: 90,
fill: 0xFFFFFF
});
andText.anchor.set(0.5, 1);
andText.alpha = 0;
andText.x = 2048 / 2;
andText.y = 2732 / 2 - 120;
game.addChild(andText);
var upitText = new Text2("Upit Studios", {
size: 180,
fill: 0xFFFFFF
});
upitText.anchor.set(0.5, 0.5);
upitText.alpha = 0;
upitText.x = 2048 / 2;
upitText.y = 2732 / 2;
game.addChild(upitText);
// 3 seconds after Gemini Games fades out (10200+600+2000+600+3000 = 16300ms)
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
tween(upitText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
tween(andText, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOut
});
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
tween(upitText, {
alpha: 0
}, {
duration: 600,
easing: tween.easeIn
});
tween(andText, {
alpha: 0
}, {
duration: 600,
easing: tween.easeIn
});
}, 2000));
}, 16300));
// After Upit Studios, show the title screen
// 3 seconds after Upit Studios fades out (16300+600+2000+600+3000 = 22400ms)
skipTimeouts.push(LK.setTimeout(function () {
if (skipIntro) return;
showTitleScreen();
}, 22400));
;
;
// --- Huge Invader Class ---
// A huge invader that splits into two smaller invaders when shot
function createHugeInvader(x, y, size, color) {
// size: 1 = normal, 2 = big, 3 = huge
var width = 120 * size;
var height = 80 * size;
var invader = LK.getAsset('box', {
width: width,
height: height,
color: color || 0xffa500,
anchorX: 0.5,
anchorY: 0.5
});
invader.x = x;
invader.y = y;
invader._vx = 1.5 + Math.random() * 1.5;
invader._vy = 0;
invader._dir = Math.random() > 0.5 ? 1 : -1;
invader._size = size;
invader._alive = true;
invader._lastX = invader.x;
invader._lastY = invader.y;
invader._isHuge = true;
return invader;
} ===================================================================
--- original.js
+++ change.js
@@ -663,13 +663,70 @@
cannonSuperBars[i].bg.y = cannon.y - 220;
cannonSuperBars[i].fill.x = cannon.x;
cannonSuperBars[i].fill.y = cannon.y - 220 + 4;
}
- // Animate super bar fill (example: pulse for demo)
+ // Animate super bar fill and show percentage
if (cannonSuperBars[i]) {
- var t = (LK.ticks + i * 30) % 120;
- var percent = 0.5 + 0.5 * Math.sin(t / 120 * Math.PI * 2);
+ // Set fill to actual value
+ var percent = superBarPercents[i];
cannonSuperBars[i].fill._setFill(percent);
+ // Show percentage text above the bar
+ if (!cannonSuperBars[i]._percentText) {
+ var pctText = new Text2("100%", {
+ size: 38,
+ fill: 0xffffff,
+ background: 0x222222,
+ padding: 8,
+ borderRadius: 12
+ });
+ pctText.anchor.set(0.5, 1);
+ pctText.x = cannonSuperBars[i].fill.x;
+ pctText.y = cannonSuperBars[i].fill.y - 18;
+ game.addChild(pctText);
+ cannonSuperBars[i]._percentText = pctText;
+ }
+ // Update percent text and position
+ if (cannonSuperBars[i]._percentText) {
+ cannonSuperBars[i]._percentText.setText(Math.floor(percent * 100) + "%");
+ cannonSuperBars[i]._percentText.x = cannonSuperBars[i].fill.x;
+ cannonSuperBars[i]._percentText.y = cannonSuperBars[i].fill.y - 18;
+ }
+ // --- Show 10s countdown over cannon if super bar is 100% and not already counting down ---
+ if (!cannonSuperBars[i]._superCountdown && percent >= 1) {
+ // Create countdown text
+ var cdText = new Text2("10", {
+ size: 90,
+ fill: 0xffee00,
+ background: 0x222222,
+ padding: 18,
+ borderRadius: 24
+ });
+ cdText.anchor.set(0.5, 0.5);
+ cdText.x = cannonSprites[i].x;
+ cdText.y = cannonSprites[i].y - 120;
+ game.addChild(cdText);
+ cannonSuperBars[i]._superCountdown = {
+ text: cdText,
+ time: 10 * 60 // 10 seconds at 60fps
+ };
+ }
+ // If counting down, update timer and text
+ if (cannonSuperBars[i]._superCountdown) {
+ var sc = cannonSuperBars[i]._superCountdown;
+ sc.text.x = cannonSprites[i].x;
+ sc.text.y = cannonSprites[i].y - 120;
+ sc.time--;
+ var secondsLeft = Math.ceil(sc.time / 60);
+ sc.text.setText(secondsLeft + "");
+ if (sc.time <= 0) {
+ // Countdown finished, remove text and reset super bar
+ if (sc.text.parent) sc.text.parent.removeChild(sc.text);
+ cannonSuperBars[i]._superCountdown = null;
+ superBarPercents[i] = 0;
+ cannonSuperBars[i].fill._setFill(0);
+ if (cannonSuperBars[i]._percentText) cannonSuperBars[i]._percentText.setText("0%");
+ }
+ }
}
if (i === 0) {
// P1 cannon: do not move by AI, position is set by hand/touch events
// Just update the name label to follow the cannon
@@ -841,8 +898,26 @@
}
inv._alive = false;
inv.parent.removeChild(inv);
}
+ // --- Check if all invaders are destroyed, then start next level ---
+ var allDead = true;
+ for (var checki = 0; checki < game._invaders.length; ++checki) {
+ if (game._invaders[checki]._alive) {
+ allDead = false;
+ break;
+ }
+ }
+ if (allDead) {
+ // Remove any remaining invader objects from stage
+ for (var remi = 0; remi < game._invaders.length; ++remi) {
+ if (game._invaders[remi].parent) game._invaders[remi].parent.removeChild(game._invaders[remi]);
+ }
+ // Next level: re-initialize invaders, increase difficulty if desired
+ game._invaders = [];
+ game._invadersInitialized = false;
+ // Optionally, you can increase invaderRows, invaderCols, or speed for more challenge
+ }
continue;
}
// Remove bullet if off screen
if (bullet.y + bullet.height < 0) {