User prompt
Make longer platforms
User prompt
Make more platforms show lifes on the up give me a background
User prompt
Give 5 lifes
User prompt
More platforms add
User prompt
Player can jump the platforms make platforms more close
User prompt
Add More platforms
User prompt
Do more slow bats
User prompt
Can you add more platforms
Code edit (1 edits merged)
Please save this source code
User prompt
Evanasense: Moonlit Spellbound
Initial prompt
Create a 2D arcade platformer game inspired by Snow Bros. The main character is a witch named Evanasense, who uses magical spells to freeze or trap enemies. The player must defeat all enemies in each level to proceed. Enemies can be immobilized with magic, then pushed or shattered to clear the area. Levels scroll vertically, with increasing difficulty and boss fights every 5 stages. Include power-ups like speed boosts, stronger magic, or temporary shields. The game should have a pixel art style with dark fantasy and gothic elements, glowing spells, and eerie backgrounds. > Add magical traps and floating platforms. Include a "full moon mode" where Evanasense becomes more powerful for a short time. Support single-player mode, with plans for local co-op in future updates.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy: Bat
var Bat = Container.expand(function (x, y) {
var self = Container.call(this);
var bat = self.attachAsset('bat', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.vx = (Math.random() > 0.5 ? 1 : -1) * (12 + Math.random() * 8);
self.vy = Math.random() > 0.5 ? 2 : -2;
self.frozen = false;
self.frozenTimer = 0;
self.trapped = false;
self.trapTimer = 0;
self.update = function () {
if (self.trapped) {
self.trapTimer--;
if (self.trapTimer <= 0) {
self.trapped = false;
self.alpha = 1;
}
return;
}
if (self.frozen) {
self.frozenTimer--;
if (self.frozenTimer <= 0) {
self.frozen = false;
bat.tint = 0x2a2a2a;
}
return;
}
self.x += self.vx;
self.y += self.vy;
if (self.x < 60 || self.x > 2048 - 60) self.vx *= -1;
if (self.y < 200 || self.y > 2732 - 200) self.vy *= -1;
};
// Freeze effect
self.freeze = function () {
self.frozen = true;
self.frozenTimer = 60;
bat.tint = 0x7fdfff;
};
// Trap effect
self.trap = function () {
self.trapped = true;
self.trapTimer = 90;
self.alpha = 0.4;
};
// Shatter (destroy)
self.shatter = function () {
LK.getSound('shatter').play();
self.destroy();
var idx = enemies.indexOf(self);
if (idx >= 0) enemies.splice(idx, 1);
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
};
return self;
});
// Evanasense (player) class
var Evanasense = Container.expand(function () {
var self = Container.call(this);
// Body
var body = self.attachAsset('evana', {
anchorX: 0.5,
anchorY: 0.5
});
// Hat
var hat = self.attachAsset('evana_hat', {
anchorX: 0.5,
anchorY: 1.1,
y: -60
});
// Physics
self.vx = 0;
self.vy = 0;
self.isOnGround = false;
self.facing = 1; // 1: right, -1: left
self.canShoot = true;
self.shootCooldown = 0;
self.shielded = false;
self.speedBoost = 0;
self.fullMoon = false;
self.fullMoonTimer = 0;
// For power-up visuals
self.shieldSprite = null;
// Freeze spell
self.castFreeze = function () {
if (!self.canShoot) return;
self.canShoot = false;
self.shootCooldown = 30; // 0.5s cooldown
var orb = new FreezeOrb(self.x, self.y - 60, self.facing);
game.addChild(orb);
freezeOrbs.push(orb);
LK.getSound('freeze').play();
};
// Trap spell (ice block, only in full moon mode)
self.castTrap = function () {
if (!self.fullMoon) return;
var trap = new IceBlock(self.x + 100 * self.facing, self.y - 40);
game.addChild(trap);
iceBlocks.push(trap);
LK.getSound('trap').play();
};
// Power-up: Shield
self.gainShield = function () {
self.shielded = true;
if (!self.shieldSprite) {
self.shieldSprite = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
}
self.shieldSprite.visible = true;
};
self.loseShield = function () {
self.shielded = false;
if (self.shieldSprite) self.shieldSprite.visible = false;
};
// Power-up: Speed
self.gainSpeed = function () {
self.speedBoost = 180;
LK.effects.flashObject(self, 0xffe066, 400);
};
// Power-up: Full Moon
self.activateFullMoon = function () {
self.fullMoon = true;
self.fullMoonTimer = 360; // 6 seconds
LK.effects.flashObject(self, 0xf6f1c7, 600);
};
// Update
self.update = function () {
// Gravity
self.vy += 2.2;
if (self.vy > 40) self.vy = 40;
// Movement
var moveSpeed = 18 + (self.speedBoost > 0 ? 10 : 0);
if (self.moveDir) {
self.vx = moveSpeed * self.moveDir;
self.facing = self.moveDir;
} else {
self.vx *= 0.7;
if (Math.abs(self.vx) < 1) self.vx = 0;
}
// Apply position
self.x += self.vx;
self.y += self.vy;
// Clamp to screen
if (self.x < 60) self.x = 60;
if (self.x > 2048 - 60) self.x = 2048 - 60;
if (self.y > 2732 - 60) {
self.y = 2732 - 60;
self.vy = 0;
self.isOnGround = true;
}
// Platform collision
self.isOnGround = false;
for (var i = 0; i < platforms.length; i++) {
var p = platforms[i];
if (self.y + 60 > p.y - p.height / 2 && self.y + 60 < p.y + p.height / 2 && self.x > p.x - p.width / 2 + 30 && self.x < p.x + p.width / 2 - 30 && self.vy >= 0) {
self.y = p.y - p.height / 2 - 60;
self.vy = 0;
self.isOnGround = true;
}
}
// Shooting cooldown
if (!self.canShoot) {
self.shootCooldown--;
if (self.shootCooldown <= 0) {
self.canShoot = true;
}
}
// Speed boost timer
if (self.speedBoost > 0) {
self.speedBoost--;
}
// Full moon timer
if (self.fullMoon) {
self.fullMoonTimer--;
if (self.fullMoonTimer <= 0) {
self.fullMoon = false;
}
}
};
return self;
});
// Freeze orb spell
var FreezeOrb = Container.expand(function (x, y, dir) {
var self = Container.call(this);
var orb = self.attachAsset('freeze_orb', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.vx = 32 * dir;
self.lifetime = 60;
self.update = function () {
self.x += self.vx;
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
var idx = freezeOrbs.indexOf(self);
if (idx >= 0) freezeOrbs.splice(idx, 1);
}
};
return self;
});
// Enemy: Ghost
var Ghost = Container.expand(function (x, y) {
var self = Container.call(this);
var ghost = self.attachAsset('ghost', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.85
});
self.x = x;
self.y = y;
self.vx = (Math.random() > 0.5 ? 1 : -1) * (8 + Math.random() * 6);
self.vy = 0;
self.frozen = false;
self.frozenTimer = 0;
self.trapped = false;
self.trapTimer = 0;
self.update = function () {
if (self.trapped) {
self.trapTimer--;
if (self.trapTimer <= 0) {
self.trapped = false;
self.alpha = 0.85;
}
return;
}
if (self.frozen) {
self.frozenTimer--;
if (self.frozenTimer <= 0) {
self.frozen = false;
ghost.tint = 0xcfd6e6;
}
return;
}
self.x += self.vx;
if (self.x < 80 || self.x > 2048 - 80) self.vx *= -1;
};
// Freeze effect
self.freeze = function () {
self.frozen = true;
self.frozenTimer = 60;
ghost.tint = 0x7fdfff;
};
// Trap effect
self.trap = function () {
self.trapped = true;
self.trapTimer = 90;
self.alpha = 0.4;
};
// Shatter (destroy)
self.shatter = function () {
LK.getSound('shatter').play();
self.destroy();
var idx = enemies.indexOf(self);
if (idx >= 0) enemies.splice(idx, 1);
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
};
return self;
});
// Ice block trap (full moon mode)
var IceBlock = Container.expand(function (x, y) {
var self = Container.call(this);
var block = self.attachAsset('ice_block', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.85
});
self.x = x;
self.y = y;
self.lifetime = 120;
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
var idx = iceBlocks.indexOf(self);
if (idx >= 0) iceBlocks.splice(idx, 1);
}
};
return self;
});
// Power-up: Moon (full moon mode)
var MoonPower = Container.expand(function (x, y) {
var self = Container.call(this);
var moon = self.attachAsset('moon', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.type = 'moon';
self.update = function () {};
return self;
});
// Platform
var Platform = Container.expand(function (x, y, w) {
var self = Container.call(this);
var plat = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
width: w || 320
});
self.x = x;
self.y = y;
self.width = w || 320;
self.height = 40;
return self;
});
// Power-up: Shield
var ShieldPower = Container.expand(function (x, y) {
var self = Container.call(this);
var shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.type = 'shield';
self.update = function () {};
return self;
});
// Power-up: Speed
var SpeedPower = Container.expand(function (x, y) {
var self = Container.call(this);
var speed = self.attachAsset('speed', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.type = 'speed';
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x18141e
});
/****
* Game Code
****/
// Game state
// Add background image
var bg = LK.getAsset('bg', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732,
x: 0,
y: 0
});
game.addChild(bg);
// Main character: Evanasense (witch)
// Witch body
// Witch hat
// Enemy: Ghost
// Enemy: Bat
// Platform
// Spell: Freeze orb
// Spell: Trap (ice block)
// Power-up: Moon
// Power-up: Shield
// Power-up: Speed
// Sound effects
// Music
var player;
var platforms = [];
var enemies = [];
var freezeOrbs = [];
var iceBlocks = [];
var powerups = [];
var stage = 1;
var stageCleared = false;
var stageTimer = 0;
var dragNode = null;
var moveStartX = 0;
var moveDir = 0;
var scoreTxt;
// Add lives
var lives = 5;
var livesTxt = new Text2('Lives: ' + lives, {
size: 70,
fill: 0xF6F1C7
});
livesTxt.anchor.set(0, 0);
LK.gui.top.addChild(livesTxt);
// Place lives at top left, but not in the 100x100 reserved area
livesTxt.x = 120;
livesTxt.y = 20;
// Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xF6F1C7
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Stage display
var stageTxt = new Text2('Stage 1', {
size: 70,
fill: 0xB8E6FF
});
stageTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(stageTxt);
stageTxt.y = 120;
// Helper: spawn platforms
function spawnPlatforms() {
// Clear old
for (var i = 0; i < platforms.length; i++) platforms[i].destroy();
platforms = [];
// Floor
var floor = new Platform(1024, 2732 - 40, 2048);
game.addChild(floor);
platforms.push(floor);
// Floating platforms
var platCount = 32 + Math.floor(stage / 0.6); // Even more platforms, super dense
for (var i = 0; i < platCount; i++) {
// Platforms are closer vertically and horizontally
var px = 100 + Math.random() * (2048 - 200);
var py = 400 + i * 70 - Math.random() * 25;
var w = 320 + Math.random() * 220;
var plat = new Platform(px, py, w);
game.addChild(plat);
platforms.push(plat);
}
}
// Helper: spawn enemies
function spawnEnemies() {
for (var i = 0; i < enemies.length; i++) enemies[i].destroy();
enemies = [];
var ghostCount = 2 + Math.floor(stage / 2);
var batCount = 3 + Math.floor(stage / 2); // Increased number of bats for more slow bats
for (var i = 0; i < ghostCount; i++) {
var px = 200 + Math.random() * (2048 - 400);
var py = 400 + Math.random() * 1200;
var g = new Ghost(px, py);
game.addChild(g);
enemies.push(g);
}
for (var i = 0; i < batCount; i++) {
var px = 200 + Math.random() * (2048 - 400);
var py = 300 + Math.random() * 1000;
var b = new Bat(px, py);
game.addChild(b);
enemies.push(b);
}
}
// Helper: spawn powerups
function spawnPowerups() {
for (var i = 0; i < powerups.length; i++) powerups[i].destroy();
powerups = [];
// Always one moon per stage
var moon = new MoonPower(200 + Math.random() * (2048 - 400), 400 + Math.random() * 1200);
game.addChild(moon);
powerups.push(moon);
// 50% chance for shield
if (Math.random() < 0.5) {
var shield = new ShieldPower(200 + Math.random() * (2048 - 400), 400 + Math.random() * 1200);
game.addChild(shield);
powerups.push(shield);
}
// 50% chance for speed
if (Math.random() < 0.5) {
var speed = new SpeedPower(200 + Math.random() * (2048 - 400), 400 + Math.random() * 1200);
game.addChild(speed);
powerups.push(speed);
}
}
// Start new stage
function startStage() {
stageCleared = false;
stageTimer = 0;
stageTxt.setText('Stage ' + stage);
LK.setScore(0);
scoreTxt.setText('0');
lives = 5;
livesTxt.setText('Lives: ' + lives);
spawnPlatforms();
spawnEnemies();
spawnPowerups();
// Place player
if (player) player.destroy();
player = new Evanasense();
player.x = 1024;
player.y = 2732 - 200;
game.addChild(player);
}
// Begin first stage
startStage();
// Touch controls
game.down = function (x, y, obj) {
// If touch is on left or right half, move
if (y > 2732 - 400) {
if (x < 1024) {
player.moveDir = -1;
} else {
player.moveDir = 1;
}
dragNode = null;
} else {
// Tap above: jump or cast
if (player.isOnGround) {
player.vy = -38;
player.isOnGround = false;
LK.getSound('jump').play();
} else {
// Cast freeze
player.castFreeze();
}
dragNode = null;
}
moveStartX = x;
};
game.up = function (x, y, obj) {
player.moveDir = 0;
dragNode = null;
};
game.move = function (x, y, obj) {
// Swipe left/right to move
if (y > 2732 - 400) {
if (x < 1024) {
player.moveDir = -1;
} else {
player.moveDir = 1;
}
} else {
player.moveDir = 0;
}
};
// Main update loop
game.update = function () {
// Update player
if (player) player.update();
// Update freeze orbs
for (var i = freezeOrbs.length - 1; i >= 0; i--) {
var orb = freezeOrbs[i];
orb.update();
// Collide with enemies
for (var j = 0; j < enemies.length; j++) {
var e = enemies[j];
if (!e.frozen && !e.trapped && orb.intersects(e)) {
e.freeze();
orb.destroy();
freezeOrbs.splice(i, 1);
break;
}
}
}
// Update ice blocks
for (var i = iceBlocks.length - 1; i >= 0; i--) {
var block = iceBlocks[i];
block.update();
// Collide with enemies
for (var j = 0; j < enemies.length; j++) {
var e = enemies[j];
if (!e.trapped && block.intersects(e)) {
e.trap();
block.destroy();
iceBlocks.splice(i, 1);
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// If frozen or trapped, can be shattered by touching again
if ((e.frozen || e.trapped) && player && player.intersects(e)) {
e.shatter();
}
// If not frozen/trapped, collision with player
if (!e.frozen && !e.trapped && player && player.intersects(e)) {
if (player.shielded) {
player.loseShield();
LK.getSound('hit').play();
LK.effects.flashObject(player, 0x8fffd6, 400);
} else {
lives--;
livesTxt.setText('Lives: ' + lives);
LK.effects.flashScreen(0x7e4a9c, 900);
if (lives <= 0) {
LK.showGameOver();
return;
} else {
// Respawn player at start position
player.x = 1024;
player.y = 2732 - 200;
player.vx = 0;
player.vy = 0;
player.loseShield();
}
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
if (player && player.intersects(p)) {
if (p.type === 'moon') {
player.activateFullMoon();
} else if (p.type === 'shield') {
player.gainShield();
} else if (p.type === 'speed') {
player.gainSpeed();
}
LK.getSound('powerup').play();
p.destroy();
powerups.splice(i, 1);
}
}
// Full moon: allow trap spell by tap with two fingers (simulate by double tap)
if (player && player.fullMoon && LK.ticks % 60 === 0) {
// For MVP, allow trap spell every second in full moon
player.castTrap();
}
// Stage clear
if (!stageCleared && enemies.length === 0) {
stageCleared = true;
stageTimer = 90;
LK.effects.flashScreen(0xb8e6ff, 600);
}
if (stageCleared) {
stageTimer--;
if (stageTimer <= 0) {
stage++;
if (stage % 5 === 0) {
// Boss stage MVP: just spawn more enemies
stageTxt.setText('Boss Stage!');
for (var i = 0; i < 3; i++) {
var px = 200 + Math.random() * (2048 - 400);
var py = 400 + Math.random() * 1200;
var g = new Ghost(px, py);
game.addChild(g);
enemies.push(g);
}
for (var i = 0; i < 2; i++) {
var px = 200 + Math.random() * (2048 - 400);
var py = 300 + Math.random() * 1000;
var b = new Bat(px, py);
game.addChild(b);
enemies.push(b);
}
stageCleared = false;
} else {
startStage();
}
}
}
};
// Play music
LK.playMusic('gothic_theme', {
fade: {
start: 0,
end: 1,
duration: 1200
}
}); ===================================================================
--- original.js
+++ change.js
@@ -437,9 +437,9 @@
for (var i = 0; i < platCount; i++) {
// Platforms are closer vertically and horizontally
var px = 100 + Math.random() * (2048 - 200);
var py = 400 + i * 70 - Math.random() * 25;
- var w = 120 + Math.random() * 180;
+ var w = 320 + Math.random() * 220;
var plat = new Platform(px, py, w);
game.addChild(plat);
platforms.push(plat);
}
Ghost. In-Game asset. 2d. High contrast. No shadows
One life potion. In-Game asset. 2d. High contrast. No shadows
Change
Witch boiler. In-Game asset. 2d. High contrast. No shadows
Diffrent colour
Broom. In-Game asset. 2d. High contrast. No shadows
Snake. In-Game asset. 2d. High contrast. No shadows
Add legs
Snowball. In-Game asset. 2d. High contrast. No shadows
Bat closed wings
Behind
Flying boss. In-Game asset. 2d. High contrast. No shadows
Fireball. In-Game asset. 2d. High contrast. No shadows
Moon. In-Game asset. 2d. High contrast. No shadows
Dark forrest. In-Game asset. 2d. High contrast. No shadows
Ice block. In-Game asset. 2d. High contrast. No shadows
Ice wall. In-Game asset. 2d. High contrast. No shadows
Portal. In-Game asset. 2d. High contrast. No shadows