User prompt
Update with: Enemy.prototype.update = function() { // ... existing enemy update code self.x -= self.speed * gameSpeedMultiplier; // ... rest of update code };
User prompt
Update with: Platform.prototype.update = function() { self.x -= PLATFORM_SPEED * gameSpeedMultiplier; if (self.x < -500) { self.destroy(); } };
User prompt
Update torch update with: child.x -= PLATFORM_SPEED * 0.6 * gameSpeedMultiplier;
User prompt
Update as needed with: function updateBackgrounds() { // Background layer (slowest) bg1.x -= PLATFORM_SPEED * 0.3 * gameSpeedMultiplier; bg2.x -= PLATFORM_SPEED * 0.3 * gameSpeedMultiplier; // ... rest of background updates mg1.x -= PLATFORM_SPEED * 0.6 * gameSpeedMultiplier; mg2.x -= PLATFORM_SPEED * 0.6 * gameSpeedMultiplier; // ... foreground updates fg1.x -= PLATFORM_SPEED * gameSpeedMultiplier; fg2.x -= PLATFORM_SPEED * gameSpeedMultiplier;
User prompt
Update with: if (self.slideTimer <= 0) { self.isSliding = false; self.hitboxHeight = self.normalHitboxHeight; self.lastSlideTime = Date.now(); // Reset speed multiplier instead of base speed gameSpeedMultiplier = 1.0; }
User prompt
Update with: self.slide = function() { var currentTime = Date.now(); if (!self.isSliding && self.isOnGround && !self.isJumping && !self.isAttacking && currentTime - self.lastSlideTime > self.slideCooldown) { self.isSliding = true; self.slideTimer = self.slideDuration; self.hitboxHeight = self.slideHitboxHeight; self.animationCounter = 0; // Adjust game speed multiplier instead of base speed gameSpeedMultiplier = self.slideSpeedMultiplier; } };
Code edit (1 edits merged)
Please save this source code
User prompt
Update as needed with: game.up = function(x, y, obj) { if (!gameStarted) { // ... existing title screen code return; } touchEndX = x; touchEndY = y; var deltaY = touchStartY - touchEndY; var deltaX = touchEndX - touchStartX; var currentTime = Date.now(); if (Math.abs(deltaX) > Math.abs(deltaY)) { // Horizontal swipe if (deltaX > 50) { // Right swipe - trigger slide player.slide(); } } else if (deltaY > 50) { // Vertical swipe - existing jump code if (currentTime - lastJumpTime > JUMP_COOLDOWN) { player.jump(); lastJumpTime = currentTime; } } else { // Tap - existing attack code player.attack(); } };
Code edit (5 edits merged)
Please save this source code
User prompt
Update player with: if (self.isSliding) { self.updateSlideAnimation(); } else if (self.isAttacking) { // ... existing animation checks }
Code edit (6 edits merged)
Please save this source code
User prompt
When doing the score popup for the health potion, instead of using the heart icon, use the heart asset with the same red tint from the health display.
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: ScorePopup is not defined' in or related to this line: 'if (child instanceof ScorePopup) {' Line Number: 1763
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: self.initGoblinSprites is not a function' in or related to this line: 'self.initGoblinSprites();' Line Number: 190
User prompt
Please fix the bug: 'TypeError: self.initGoblinSprites is not a function' in or related to this line: 'self.initGoblinSprites();' Line Number: 190
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: game.up = function(x, y, obj) { if (!gameStarted) { var buttonBounds = { left: playButton.x - playButton.width/2, right: playButton.x + playButton.width/2, top: playButton.y - playButton.height/2, bottom: playButton.y + playButton.height/2 }; if (x >= buttonBounds.left && x <= buttonBounds.right && y >= buttonBounds.top && y <= buttonBounds.bottom) { startGame(); } return; } touchEndX = x; touchEndY = y; var deltaY = touchStartY - touchEndY; var deltaX = Math.abs(touchStartX - touchEndX); var currentTime = Date.now(); if (deltaY > 50 && deltaY > deltaX) { if (currentTime - lastJumpTime > jumpCooldown) { player.jump(); lastJumpTime = currentTime; } } else { // Only attack if it wasn't a jump attempt player.attack(); } };
Code edit (3 edits merged)
Please save this source code
User prompt
Update with: game.move = function(x, y, obj) { if (!gameStarted) return; if (lastMoveY !== 0) { var deltaY = y - lastMoveY; var currentTime = Date.now(); if (deltaY < -moveThreshold && currentTime - lastJumpTime > jumpCooldown) { player.jump(); lastJumpTime = currentTime; } } lastMoveY = y; };
User prompt
Update with: game.up = function(x, y, obj) { if (!gameStarted) { var buttonBounds = { left: playButton.x - playButton.width/2, right: playButton.x + playButton.width/2, top: playButton.y - playButton.height/2, bottom: playButton.y + playButton.height/2 }; if (x >= buttonBounds.left && x <= buttonBounds.right && y >= buttonBounds.top && y <= buttonBounds.bottom) { startGame(); } return; } touchEndX = x; touchEndY = y; var deltaY = touchStartY - touchEndY; var deltaX = Math.abs(touchStartX - touchEndX); var currentTime = Date.now(); if (deltaY > 50 && deltaY > deltaX && currentTime - lastJumpTime > jumpCooldown) { player.jump(); lastJumpTime = currentTime; } else { player.attack(); } };
Code edit (1 edits merged)
Please save this source code
User prompt
Replace game.move with: game.move = function(x, y, obj) { if (!gameStarted) return; if (lastMoveY !== 0) { var deltaY = y - lastMoveY; if (deltaY < -moveThreshold) { player.jump(); } } lastMoveY = y; };
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'moveTimeout = setTimeout(function () {' Line Number: 1784
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Refactored classes - keeping original Container.expand pattern var Coin = Container.expand(function (type) { var self = Container.call(this); // Set type and create sprite self.type = type || 'coin'; self.sprite = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5, tint: 0xFFFFFF }); // Initialize physics properties CollectibleBehavior.initPhysics(self); // Get value based on type self.getValue = function () { switch (self.type) { case 'diamond': return 10; case 'emerald': return 5; case 'ruby': return 3; default: return 1; } }; // Collection functionality self.collect = function () { if (!self.collected) { self.collected = true; var value = self.getValue(); scoreManager.addScore(value, self.x, self.y); LK.getSound('coincollect').play(); self.destroy(); } }; // Standard update method using shared behavior self.update = function () { CollectibleBehavior.standardUpdate(self); }; self.checkPlatformCollision = function () { return GameUtils.checkPlatformCollision(self, 80, true) != null; }; return self; }); // Enemy class with refactored animation management var Enemy = Container.expand(function (type) { var self = Container.call(this); // Enemy properties self.type = type || 'basic'; self.speed = 7; self.isOnGround = true; self.velocityY = 0; self.currentPlatform = null; self.groundY = GAME_HEIGHT / 1.5; self.isHit = false; self.isDying = false; self.deathTimer = 0; self.throwBackSpeed = 15; self.throwBackDistance = 0; self.maxThrowBack = 200; // Hitbox properties self.hitboxWidth = 200; self.hitboxHeight = self.type === 'eyeball' ? 90 : 260; // Animation properties self.sprites = []; self.animationCounter = 0; self.animationSpeed = 0.08; // Initialize based on enemy type if (self.type === 'eyeball') { // Eyeball-specific properties self.isFlying = true; self.flyingHeight = 0; self.verticalSpeed = 2; self.maxVerticalSpeed = 4; self.homingDelay = 80; self.homingTimer = 0; self.flyFrame = 0; // Animation frames self.flyAnimation = ['eyefly1', 'eyefly2', 'eyefly3', 'eyefly4', 'eyefly5', 'eyefly6', 'eyefly7', 'eyefly8']; self.hitAnimation = ['eyedie1', 'eyedie2']; self.dieAnimation = ['eyedie3', 'eyedie4', 'eyedie5']; // Initialize animation sprites self.initEyeballSprites(); } else if (self.type === 'goblin') { // Goblin-specific properties self.runFrame = 0; // Animation frames self.runAnimation = ['goblinrun1', 'goblinrun2', 'goblinrun3', 'goblinrun4', 'goblinrun5', 'goblinrun6', 'goblinrun7', 'goblinrun8']; self.hitAnimation = ['goblinhit1']; self.dieAnimation = ['goblindie1', 'goblindie2', 'goblindie3', 'goblindie4']; // Initialize animation sprites self.initGoblinSprites(); } else { // Basic enemy var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); } // Initialize animation sprites for eyeball self.initEyeballSprites = function () { // Add fly animations for (var i = 0; i < self.flyAnimation.length; i++) { var sprite = self.attachAsset(self.flyAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } // Add hit animations for (var i = 0; i < self.hitAnimation.length; i++) { var sprite = self.attachAsset(self.hitAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } // Add die animations for (var i = 0; i < self.dieAnimation.length; i++) { var sprite = self.attachAsset(self.dieAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } }; // Initialize animation sprites for goblin self.initGoblinSprites = function () { // Add run animations for (var i = 0; i < self.runAnimation.length; i++) { var sprite = self.attachAsset(self.runAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } // Add hit animations for (var i = 0; i < self.hitAnimation.length; i++) { var sprite = self.attachAsset(self.hitAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } // Add die animations for (var i = 0; i < self.dieAnimation.length; i++) { var sprite = self.attachAsset(self.dieAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } }; // Hide all animation sprites self.hideAllSprites = function () { for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } }; // Update eyeball hit/die animation self.updateEyeballDamageState = function () { if (self.isHit) { var hitOffset = self.flyAnimation.length; self.throwBackDistance += Math.abs(self.throwBackSpeed); self.x += self.throwBackSpeed; self.throwBackSpeed *= 0.95; // Show hit animation var hitFrame = Math.floor(self.hitTimer / 100) % 2; self.sprites[hitOffset + hitFrame].alpha = 1; self.hitTimer--; if (self.hitTimer <= 0) { self.isHit = false; self.isDying = true; self.deathTimer = 180; self.deathFrame = 0; } } else if (self.isDying) { var dieOffset = self.flyAnimation.length + self.hitAnimation.length; // Always check for ground collision during fall if (!self.isOnGround) { self.velocityY += 0.5; self.y += self.velocityY; } // Progress through death frames if (self.deathTimer > 120) { self.sprites[dieOffset].alpha = 1; // eyedie3 } else if (self.deathTimer > 60) { self.sprites[dieOffset + 1].alpha = 1; // eyedie4 } else { self.sprites[dieOffset + 2].alpha = 1; // eyedie5 } self.deathTimer--; if (self.deathTimer <= 0) { self.alpha -= 0.05; if (self.alpha <= 0) { self.destroy(); } } } }; // Update eyeball normal movement self.updateEyeballNormalState = function () { self.x -= self.speed; // Only start homing after delay if (self.homingTimer >= self.homingDelay) { // Home toward player var deltaY = player.y - self.y; self.velocityY += deltaY > 0 ? 0.2 : -0.2; self.velocityY = Math.max(-self.maxVerticalSpeed, Math.min(self.maxVerticalSpeed, self.velocityY)); } else { // Before homing, maintain height with slight wave motion self.velocityY = Math.sin(self.homingTimer * 0.05) * 2; self.homingTimer++; } self.y += self.velocityY; // Animate self.animationCounter += self.animationSpeed; if (self.animationCounter >= 1) { self.animationCounter = 0; self.flyFrame = (self.flyFrame + 1) % self.flyAnimation.length; } self.sprites[self.flyFrame].alpha = 1; }; // Update goblin hit animation self.updateGoblinHitState = function () { // Handle throw back motion self.x += self.throwBackSpeed; self.throwBackDistance += Math.abs(self.throwBackSpeed); self.throwBackSpeed *= 0.95; // Show hit animation var hitOffset = self.runAnimation.length; self.sprites[hitOffset].alpha = 1; // Decrease hit timer self.hitTimer--; // Once hit timer expires, start death animation if (self.hitTimer <= 0) { self.isHit = false; self.isDying = true; self.deathTimer = 60; self.deathFrame = 0; } }; // Update goblin dying animation self.updateGoblinDyingState = function () { // Continue throw back during death self.x += self.throwBackSpeed; // After halfway through death animation, match platform speed if (self.deathFrame >= 2) { self.x -= PLATFORM_SPEED; } self.throwBackSpeed *= 0.95; // Handle death animation var dieOffset = self.runAnimation.length + self.hitAnimation.length; // Progress frame every 15 frames if (self.deathTimer % 15 === 0 && self.deathFrame < self.dieAnimation.length - 1) { self.deathFrame++; } self.sprites[dieOffset + self.deathFrame].alpha = 1; // Count down death timer self.deathTimer--; // After timer expires, fade out if (self.deathTimer <= 0) { self.alpha -= 0.1; if (self.alpha <= 0) { self.destroy(); } } }; // Update goblin normal movement self.updateGoblinNormalState = function () { // Move left self.x -= self.speed; // Platform and gravity code if (!self.isOnGround) { self.velocityY += 0.7; self.y += self.velocityY; self.checkPlatformCollision(); } // Ensure goblin stays on platform if (self.currentPlatform) { var stillOnPlatform = self.x >= self.currentPlatform.x - PLATFORM_HALF_WIDTH && self.x <= self.currentPlatform.x + PLATFORM_HALF_WIDTH; if (!stillOnPlatform) { var foundAnotherPlatform = false; // Check for another platform for (var i = 0; i < platforms.length; i++) { var otherPlatform = platforms[i]; if (otherPlatform === self.currentPlatform) { continue; } if (self.x >= otherPlatform.x - PLATFORM_HALF_WIDTH && self.x <= otherPlatform.x + PLATFORM_HALF_WIDTH && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) { self.currentPlatform = otherPlatform; foundAnotherPlatform = true; break; } } // Start falling if no other platform found if (!foundAnotherPlatform) { self.isOnGround = false; self.currentPlatform = null; if (self.velocityY === 0) { self.velocityY = 0.1; } } } } // Animate running self.animationCounter += self.animationSpeed; if (self.animationCounter >= 1) { self.animationCounter = 0; self.runFrame = (self.runFrame + 1) % self.runAnimation.length; } self.sprites[self.runFrame].alpha = 1; }; // Check platform collision self.checkPlatformCollision = function () { var onAnyPlatform = false; for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; var leftEdge = platform.x - PLATFORM_HALF_WIDTH; var rightEdge = platform.x + PLATFORM_HALF_WIDTH; // Check if we're on a platform (exact position check) if (self.x >= leftEdge && self.x <= rightEdge) { if (Math.abs(self.y - (platform.y - ENEMY_PLATFORM_OFFSET)) < 5) { onAnyPlatform = true; self.currentPlatform = platform; self.y = platform.y - ENEMY_PLATFORM_OFFSET; self.isOnGround = true; self.velocityY = 0; return true; } // Check for passing through platform during fall if (self.velocityY > 0 && self.y < platform.y - ENEMY_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - ENEMY_PLATFORM_OFFSET) { self.y = platform.y - ENEMY_PLATFORM_OFFSET; self.velocityY = 0; self.isOnGround = true; self.currentPlatform = platform; return true; } } } if (!onAnyPlatform) { self.isOnGround = false; self.currentPlatform = null; } return false; }; // Get collision bounds self.getBounds = function () { return { left: self.x - self.hitboxWidth / 2, right: self.x + self.hitboxWidth / 2, top: self.y - self.hitboxHeight / 2, bottom: self.y + self.hitboxHeight / 2 }; }; // Hit handling self.hit = function () { if (!self.isHit && !self.isDying) { self.isHit = true; self.throwBackSpeed = 25; self.throwBackDistance = 0; self.hitTimer = 35; // Add particle effect var particleOffset = self.type === 'eyeball' ? 175 : 250; particleSystem.emitFromHit(self.x + particleOffset, self.y, player.x); } }; // Main update method self.update = function () { // Hide all sprites first self.hideAllSprites(); // Handle different enemy types and states if (self.type === 'eyeball') { if (self.isHit || self.isDying) { self.updateEyeballDamageState(); } else { self.updateEyeballNormalState(); } } else { // Goblin logic if (self.isHit) { self.updateGoblinHitState(); } else if (self.isDying) { self.updateGoblinDyingState(); } else { self.updateGoblinNormalState(); } } // Destroy if off screen if (self.x < -50 || self.y > GAME_HEIGHT) { self.destroy(); } }; return self; }); var HealthPotion = Container.expand(function () { var self = Container.call(this); // Create sprite self.sprite = self.attachAsset('healthpotion', { anchorX: 0.5, anchorY: 0.8 }); // Initialize physics properties CollectibleBehavior.initPhysics(self); // Collection functionality self.collect = function () { if (!self.collected && player.currentHealth < player.maxHealth) { self.collected = true; player.currentHealth++; player.heartContainer.updateHealth(player.currentHealth); player.heartContainer.alpha = 1; player.heartVisibilityTimer = player.heartVisibilityDuration; // Create heart popup var popup = new Text2('ā¤ļø', { size: 80, fill: 0xFF0000, anchorX: 0.5, anchorY: 0.5 }); popup.x = self.x; popup.y = self.y - 30; popup.velocityY = -3; popup.lifespan = 45; popup.update = function () { this.y += this.velocityY; this.lifespan--; if (this.lifespan < 15) { this.alpha -= 0.07; } if (this.alpha <= 0 || this.lifespan <= 0) { this.destroy(); } }; game.addChild(popup); LK.getSound('potion').play(); self.destroy(); } }; // Standard update using shared behavior self.update = function () { CollectibleBehavior.standardUpdate(self); }; self.checkPlatformCollision = function () { return GameUtils.checkPlatformCollision(self, 80, true) != null; }; return self; }); var HeartContainer = Container.expand(function () { var self = Container.call(this); self.maxHealth = 3; self.currentHealth = 3; self.hearts = []; // Calculate total width of heart display var heartSpacing = 80; var totalWidth = (self.maxHealth - 1) * heartSpacing; // Initialize hearts with centered positioning for (var i = 0; i < self.maxHealth; i++) { var heart = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, tint: 0xCC0000 }); heart.x = i * heartSpacing - totalWidth / 2; heart.y = 0; self.hearts.push(heart); } // Start invisible self.alpha = 0; self.updateHealth = function (newHealth) { self.currentHealth = newHealth; // Update heart display for (var i = 0; i < self.maxHealth; i++) { if (i < newHealth) { // Full heart self.hearts[i].tint = 0xFF0000; self.hearts[i].alpha = 1; } else { // Empty heart self.hearts[i].tint = 0x000000; self.hearts[i].alpha = 0.5; } } }; return self; }); var Jar = Container.expand(function () { var self = Container.call(this); // Attach jar sprite self.sprite = self.attachAsset('jar', { anchorX: 0.5, anchorY: 0.5, tint: 0xC0C0C0 }); // Initialize as breakable BreakableBehavior.initBreakable(self); // Break functionality self["break"] = function () { BreakableBehavior.standardBreak(self, JarPiece, 4, function (jar) { // Spawn health potion with low chance if (Math.random() < 0.05) { var potion = new HealthPotion(); potion.x = jar.x; potion.y = jar.y; potion.velocityX = GameUtils.randomRange(4, 12); potion.velocityY = -GameUtils.randomRange(12, 22); game.addChild(potion); coins.push(potion); } // Spawn coins var coinCount = Math.floor(GameUtils.randomRange(1, 9)); for (var i = 0; i < coinCount; i++) { var coin = new Coin(); coin.x = jar.x; coin.y = jar.y; coin.velocityX = GameUtils.randomRange(4, 12); coin.velocityY = -GameUtils.randomRange(12, 22); game.addChild(coin); coins.push(coin); } LK.getSound('jarbreak').play(); }); }; return self; }); var JarPiece = Container.expand(function (pieceNum) { var self = Container.call(this); // Attach piece sprite self.sprite = self.attachAsset('jarpiece' + pieceNum, { anchorX: 0.5, anchorY: 0.5, tint: 0xC0C0C0 }); // Initialize as piece PieceBehavior.initPiece(self); // Update method self.update = function () { PieceBehavior.standardUpdate(self); }; self.checkPlatformCollision = function () { return GameUtils.checkPlatformCollision(self, 80, true) != null; }; return self; }); var ParticlePool = Container.expand(function (maxParticles) { var self = Container.call(this); self.particles = []; self.activeParticles = []; self.redTints = [0xff0000, 0xff3333, 0xcc0000]; for (var i = 0; i < maxParticles; i++) { var particle = self.attachAsset('pixel', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2 }); particle.alpha = 0; particle.velocityX = 0; particle.velocityY = 0; particle.lifespan = 0; particle.fadeSpeed = 0; self.particles.push(particle); } self.emitFromHit = function (x, y, playerX) { var directionX = x - playerX; var directionSign = Math.sign(directionX); for (var i = 0; i < 20; i++) { if (self.particles.length === 0) { break; } var particle = self.particles.pop(); self.activeParticles.push(particle); particle.x = x; particle.y = y; particle.alpha = 1; particle.tint = self.redTints[Math.floor(Math.random() * self.redTints.length)]; // Set scale var particleSize = Math.random() * 0.2 + 0.2; particle.scaleX = particleSize; particle.scaleY = particleSize; var angle = Math.random() * Math.PI / 2 - Math.PI / 4; var speed = Math.random() * 5 + 10; particle.velocityX = Math.cos(angle) * speed * directionSign; particle.velocityY = Math.sin(angle) * speed; particle.lifespan = 100; particle.fadeSpeed = 1 / 60; } }; self.update = function () { for (var i = self.activeParticles.length - 1; i >= 0; i--) { var particle = self.activeParticles[i]; particle.x += particle.velocityX; particle.y += particle.velocityY; particle.alpha -= particle.fadeSpeed; particle.lifespan--; if (particle.lifespan <= 0 || particle.alpha <= 0) { particle.alpha = 0; self.activeParticles.splice(i, 1); self.particles.push(particle); } } }; return self; }); var Platform = Container.expand(function () { var self = Container.call(this); var platformGraphics = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); self.speed = PLATFORM_SPEED; self.passed = false; self.update = function () { self.x -= self.speed; if (self.x < -500) { self.destroy(); } }; return self; }); // Player class with refactored animation management var Player = Container.expand(function () { var self = Container.call(this); // Animation properties self.runAnimation = ['playerrun1', 'playerrun2', 'playerrun3', 'playerrun4', 'playerrun5', 'playerrun6']; self.jumpAnimation = ['playerjump1', 'playerjump2', 'playerjump3']; self.attackAnimation = ['playerattack1', 'playerattack2', 'playerattack3', 'playerattack4', 'playerattack5']; // Animation states self.isAttacking = false; self.attackFrame = 0; self.runFrame = 0; self.animationSpeed = 0.08; self.attackAnimationSpeed = 0.15; self.animationCounter = 0; self.sprites = []; // Physics properties self.groundY = GAME_HEIGHT * 0.9; self.hitboxWidth = 150; self.hitboxHeight = 300; self.attackHitboxWidth = 200; self.attackHitboxHeight = 400; self.attackHitboxOffset = 50; // Platform collision properties self.isOnGround = true; self.currentPlatform = null; // Health properties self.heartContainer = heartContainer; self.maxHealth = 3; self.currentHealth = 3; self.isInvulnerable = false; self.invulnerabilityDuration = 90; self.invulnerabilityTimer = 0; self.heartVisibilityTimer = 0; self.heartVisibilityDuration = 120; // Movement properties self.speed = 5; self.jumpHeight = 40; self.isJumping = false; self.velocityY = 0; self.jumpState = "none"; self.jumpStartTime = 0; // Initialize animation sprites self.initAnimations = function () { // Run animations for (var i = 0; i < self.runAnimation.length; i++) { var sprite = self.attachAsset(self.runAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } // Jump animations for (var i = 0; i < self.jumpAnimation.length; i++) { var sprite = self.attachAsset(self.jumpAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } // Attack animations for (var i = 0; i < self.attackAnimation.length; i++) { var sprite = self.attachAsset(self.attackAnimation[i], { anchorX: 0.5, anchorY: 0.5 }); sprite.alpha = 0; self.sprites.push(sprite); } }; // Call initialization self.initAnimations(); // Get collision bounds self.getBounds = function () { return { left: self.x - self.hitboxWidth / 2, right: self.x + self.hitboxWidth / 2, top: self.y - self.hitboxHeight / 2, bottom: self.y + self.hitboxHeight / 2 }; }; // Get attack hitbox self.getAttackBounds = function () { if (!self.isAttacking) { return null; } return { left: self.x + (self.attackHitboxOffset - self.attackHitboxWidth / 2), right: self.x + (self.attackHitboxOffset + self.attackHitboxWidth / 2), top: self.y - self.attackHitboxHeight / 2, bottom: self.y + self.attackHitboxHeight / 2 }; }; // Update heart container self.updateHeartContainer = function () { self.heartContainer.x = self.x + 80; self.heartContainer.y = self.y - 200; // Handle heart visibility if (self.heartVisibilityTimer > 0) { self.heartVisibilityTimer--; if (self.heartVisibilityTimer <= 0) { self.heartContainer.alpha = 0; } } }; // Update invulnerability state self.updateInvulnerability = function () { if (self.isInvulnerable) { self.invulnerabilityTimer--; // Flash only the player, not the hearts self.alpha = self.invulnerabilityTimer % 10 < 5 ? 0.5 : 1; if (self.invulnerabilityTimer <= 0) { self.isInvulnerable = false; self.alpha = 1; } } }; // Handle platform collision self.handlePlatformCollision = function () { // Already checks in jumpstate if (!self.isOnGround && !self.isJumping) { self.velocityY += 0.7; self.y += self.velocityY; self.checkPlatformCollision(); } // Check if on any platform var onAnyPlatform = false; for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; var leftEdge = platform.x - PLATFORM_HALF_WIDTH; var rightEdge = platform.x + PLATFORM_HALF_WIDTH; // Check if player is within horizontal bounds of platform if (self.x >= leftEdge && self.x <= rightEdge) { // If we're at platform height and not jumping if (Math.abs(self.y - (platform.y - PLAYER_PLATFORM_OFFSET)) < 5 && !self.isJumping) { onAnyPlatform = true; self.currentPlatform = platform; self.y = platform.y - PLAYER_PLATFORM_OFFSET; self.isOnGround = true; self.velocityY = 0; break; } } } // Handle falling state if (!onAnyPlatform && !self.isJumping) { self.isOnGround = false; self.currentPlatform = null; // Apply gravity if (self.velocityY === 0) { self.velocityY = 0.1; } self.velocityY += 0.1; self.y += self.velocityY; // Ground collision check comes AFTER falling movement if (self.y >= self.groundY) { // Reached the bottom of the screen - game over LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } // If on a platform, check if still above it if (self.currentPlatform) { var stillOnPlatform = self.x > self.currentPlatform.x - PLATFORM_HALF_WIDTH && self.x < self.currentPlatform.x + PLATFORM_HALF_WIDTH; if (!stillOnPlatform) { var foundAnotherPlatform = false; for (var i = 0; i < platforms.length; i++) { var otherPlatform = platforms[i]; if (otherPlatform === self.currentPlatform) { continue; } if (self.x > otherPlatform.x - PLATFORM_HALF_WIDTH && self.x < otherPlatform.x + PLATFORM_HALF_WIDTH) { // Found another platform self.currentPlatform = otherPlatform; foundAnotherPlatform = true; break; } } // Fall if no other platform found if (!foundAnotherPlatform) { self.isOnGround = false; self.currentPlatform = null; self.velocityY = 0.1; } } } }; // Update attack animation self.updateAttackAnimation = function () { var attackOffset = self.runAnimation.length + self.jumpAnimation.length; self.animationCounter += self.attackAnimationSpeed; if (self.animationCounter >= 1) { self.animationCounter = 0; self.attackFrame++; if (self.attackFrame >= self.attackAnimation.length) { self.isAttacking = false; self.attackFrame = 0; } } self.sprites[attackOffset + self.attackFrame].alpha = 1; }; // Update jump animation self.updateJumpAnimation = function () { var jumpOffset = self.runAnimation.length; var currentTime = Date.now(); self.y += self.velocityY; self.velocityY += 0.7; // Check for landing on platforms while falling self.checkPlatformCollision(); // End jump if hitting the ground if (self.y >= self.groundY && !self.currentPlatform) { self.y = self.groundY; self.isJumping = false; self.velocityY = 0; self.jumpState = "none"; self.isOnGround = true; } // Show appropriate jump frame if (currentTime - self.jumpStartTime < 100) { self.sprites[jumpOffset + 0].alpha = 1; } else if (self.velocityY < 0) { self.sprites[jumpOffset + 1].alpha = 1; } else if (self.velocityY > 0) { self.sprites[jumpOffset + 2].alpha = 1; } }; // Update run animation self.updateRunAnimation = function () { self.animationCounter += self.animationSpeed; if (self.animationCounter >= 1) { self.animationCounter = 0; self.runFrame = (self.runFrame + 1) % self.runAnimation.length; } self.sprites[self.runFrame].alpha = 1; }; // Check platform collision self.checkPlatformCollision = function () { for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; // Check if player is above the platform and falling if (self.velocityY > 0 && self.y < platform.y - PLAYER_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - PLAYER_PLATFORM_OFFSET && self.x > platform.x - PLATFORM_HALF_WIDTH && self.x < platform.x + PLATFORM_HALF_WIDTH) { // Land on the platform self.y = platform.y - PLAYER_PLATFORM_OFFSET; self.velocityY = 0; self.isJumping = false; self.isOnGround = true; self.currentPlatform = platform; return; } } }; // Hide all sprites self.hideAllSprites = function () { for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } }; // Jump method self.jump = function () { if (self.isOnGround) { self.isJumping = true; self.isOnGround = false; self.velocityY = -self.jumpHeight; self.jumpState = "start"; self.jumpStartTime = Date.now(); LK.getSound('playerjump').play(); self.currentPlatform = null; } else if (self.isJumping && self.velocityY < 10) { // Small double-jump to reach higher platforms self.velocityY = -self.jumpHeight * 0.7; self.jumpStartTime = Date.now(); } }; // Attack method self.attack = function () { if (!self.isAttacking) { self.isAttacking = true; self.attackFrame = 0; self.animationCounter = 0; LK.getSound('swordslash').play(); } }; // Take damage method self.takeDamage = function () { if (!self.isInvulnerable) { self.currentHealth--; self.heartContainer.updateHealth(self.currentHealth); // Show hearts and set visibility timer self.heartContainer.alpha = 1; self.heartVisibilityTimer = self.heartVisibilityDuration; // Visual feedback self.isInvulnerable = true; self.invulnerabilityTimer = self.invulnerabilityDuration; // Flash player red self.tint = 0xFF0000; tween(self, { tint: 0xFFFFFF }, { duration: 500, easing: tween.easeOut }); if (self.currentHealth <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } }; // Main update method self.update = function () { // Update heart container position self.updateHeartContainer(); // Hide all sprites self.hideAllSprites(); // Handle platform collision and falling self.handlePlatformCollision(); // Handle invulnerability self.updateInvulnerability(); // Update animations based on state if (self.isAttacking) { self.updateAttackAnimation(); } else if (self.isJumping || !self.isOnGround) { self.updateJumpAnimation(); } else if (self.isOnGround) { self.updateRunAnimation(); } // Check for falling off screen if (self.y > GAME_HEIGHT) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } }; return self; }); var ScorePopup = Container.expand(function (x, y, amount) { var self = Container.call(this); // Create the text self.text = new Text2('+' + amount, { size: 80, fill: 0xFFFFFF, anchorX: 0.5, anchorY: 0.5 }); self.addChild(self.text); self.x = x; self.y = y; self.velocityY = -3; self.lifespan = 45; self.update = function () { self.y += self.velocityY; self.lifespan--; if (self.lifespan < 15) { self.alpha -= 0.07; } if (self.alpha <= 0 || self.lifespan <= 0) { self.destroy(); } }; return self; }); /**** * Constants ****/ var Torch = Container.expand(function () { var self = Container.call(this); // Create base torch sprite self.base = self.attachAsset('torch', { anchorX: 0.5, anchorY: 1 }); // Create flame sprite self.flame = self.attachAsset('torchflame', { anchorX: 0.5, anchorY: 1, y: -180 }); // Create aura sprite self.aura = self.attachAsset('torchaura', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, y: -250 }); // Animation properties self.flameTime = Math.random() * Math.PI * 2; self.auraTime = Math.random() * Math.PI * 2; self.flameSpeed = 0.05; self.auraSpeed = 0.03; // Update animation self.update = function () { // Animate flame scale self.flameTime += self.flameSpeed; var flameScale = 1 + Math.sin(self.flameTime) * 0.2; self.flame.scaleY = flameScale; // Random flip chance for flame if (Math.random() < 0.02) { self.flame.scaleX *= -1; } // Animate aura alpha self.auraTime += self.auraSpeed; var auraAlpha = 0.3 + Math.sin(self.auraTime) * 0.15; self.aura.alpha = auraAlpha; }; return self; }); var TreasureChest = Container.expand(function () { var self = Container.call(this); // Attach chest sprite self.sprite = self.attachAsset('treasurechest', { anchorX: 0.5, anchorY: 0.5, tint: 0xC0C0C0 }); // Initialize as breakable BreakableBehavior.initBreakable(self); // Break functionality self["break"] = function () { BreakableBehavior.standardBreak(self, TreasureChestPiece, 4, function (chest) { // Spawn health potion with medium chance if (Math.random() < 0.25) { var potion = new HealthPotion(); potion.x = chest.x; potion.y = chest.y; potion.velocityX = GameUtils.randomRange(2, 12); potion.velocityY = -GameUtils.randomRange(14, 26); game.addChild(potion); coins.push(potion); } // Spawn valuable items var totalItems = Math.floor(GameUtils.randomRange(3, 9)); for (var i = 0; i < totalItems; i++) { // Random chance for different gems var rand = Math.random(); var item; if (rand < 0.05) { // 5% diamond item = new Coin('diamond'); } else if (rand < 0.15) { // 10% emerald item = new Coin('emerald'); } else if (rand < 0.30) { // 15% ruby item = new Coin('ruby'); } else { // 70% gold coin item = new Coin('coin'); } item.x = chest.x; item.y = chest.y; item.velocityX = GameUtils.randomRange(2, 12); item.velocityY = -GameUtils.randomRange(14, 26); game.addChild(item); coins.push(item); } LK.getSound('woodbreak').play(); }); }; return self; }); var TreasureChestPiece = Container.expand(function (pieceNum) { var self = Container.call(this); // Attach piece sprite self.sprite = self.attachAsset('treasurechestpiece' + pieceNum, { anchorX: 0.5, anchorY: 0.5, tint: 0xC0C0C0 }); // Initialize as piece PieceBehavior.initPiece(self); // Update method self.update = function () { PieceBehavior.standardUpdate(self); }; self.checkPlatformCollision = function () { return GameUtils.checkPlatformCollision(self, 80, true) != null; }; return self; }); /**** * Initialize Game ****/ /**** * Game Initialization ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Base collectible behavior for items that can be collected /**** * Game Management ****/ var CollectibleBehavior = { initPhysics: function initPhysics(self) { self.velocityX = 0; self.velocityY = 0; self.collected = false; self.bounceCount = 0; self.maxBounces = 2; }, standardUpdate: function standardUpdate(self) { if (self.collected) { return; } // Apply physics self.velocityY += 0.5; // gravity self.x += self.velocityX; self.y += self.velocityY; // Check for platform collision with bounce if (self.velocityY > 0) { GameUtils.checkPlatformCollision(self, 80, true); } // Check if off screen if (self.x < -50 || self.x > GAME_WIDTH + 50 || self.y > GAME_HEIGHT) { self.destroy(); return; } // Player collection detection var playerBounds = player.getBounds(); var itemBounds = { left: self.x - 25, right: self.x + 25, top: self.y - 25, bottom: self.y + 25 }; if (GameUtils.checkCollision(playerBounds, itemBounds)) { self.collect(); } } }; // Base behavior for breakable objects var BreakableBehavior = { initBreakable: function initBreakable(self) { self.isBreaking = false; self.currentPlatform = null; }, standardBreak: function standardBreak(self, pieceClass, pieceCount, itemSpawnCallback) { if (self.isBreaking) { return; } self.isBreaking = true; // Spawn pieces for (var i = 1; i <= pieceCount; i++) { var piece = new pieceClass(i); piece.x = self.x; piece.y = self.y; piece.velocityX = GameUtils.randomRange(-6, 6); piece.velocityY = -GameUtils.randomRange(6, 12); piece.rotationSpeed = GameUtils.randomRange(-0.1, 0.1); game.addChild(piece); } // Call the custom item spawn callback if (itemSpawnCallback) { itemSpawnCallback(self); } self.destroy(); } }; // Base behavior for pieces of broken objects var PieceBehavior = { initPiece: function initPiece(self) { self.velocityX = 0; self.velocityY = 0; self.rotationSpeed = 0; self.fadeSpeed = 0.02; self.bounceCount = 0; self.maxBounces = 2; }, standardUpdate: function standardUpdate(self) { // Apply physics self.velocityY += 0.5; // gravity self.x += self.velocityX; self.y += self.velocityY; self.rotation += self.rotationSpeed; // Check for platform collision var platformCollision = GameUtils.checkPlatformCollision(self, 80, self.bounceCount < self.maxBounces); if (platformCollision && self.bounceCount >= self.maxBounces) { // Start fading after max bounces self.velocityY = 0; self.velocityX = -PLATFORM_SPEED; self.alpha -= self.fadeSpeed; if (self.alpha <= 0) { self.destroy(); } } // Destroy if off screen if (self.x < -50 || self.y > GAME_HEIGHT) { self.destroy(); } } }; /**** * Constants ****/ var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var PLATFORM_WIDTH = 1000; var PLATFORM_HALF_WIDTH = PLATFORM_WIDTH / 2; var PLATFORM_OVERLAP = 50; var PLATFORM_SPEED = 5; var ENEMY_PLATFORM_OFFSET = 225; var PLAYER_PLATFORM_OFFSET = 250; var MIN_PLATFORMS_IN_SEQUENCE = 2; var MAX_PLATFORMS_IN_SEQUENCE = 5; var JUMP_COOLDOWN = 250; var MOVE_THRESHOLD = 40; /**** * Utilities ****/ var GameUtils = { // Check for collision between two rectangular bounds checkCollision: function checkCollision(bounds1, bounds2) { return bounds1.left < bounds2.right && bounds1.right > bounds2.left && bounds1.top < bounds2.bottom && bounds1.bottom > bounds2.top; }, // Check platform collision with standard offset checkPlatformCollision: function checkPlatformCollision(obj, offsetY, bounceOnCollision) { for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; if (Math.abs(obj.y - (platform.y - offsetY)) < 10 && obj.x > platform.x - PLATFORM_HALF_WIDTH && obj.x < platform.x + PLATFORM_HALF_WIDTH) { obj.y = platform.y - offsetY; if (bounceOnCollision && obj.bounceCount < obj.maxBounces) { LK.getSound('coinbounce').play(); var impactSpeed = Math.abs(obj.velocityY); obj.velocityY = -(impactSpeed * 0.5); obj.velocityX *= 0.8; obj.bounceCount++; return true; } else if (bounceOnCollision) { obj.velocityY = 0; obj.velocityX = -PLATFORM_SPEED; } return platform; } } return null; }, // Get random value within range randomRange: function randomRange(min, max) { return Math.random() * (max - min) + min; }, // Check if position is clear for spawning canSpawnAtPosition: function canSpawnAtPosition(x) { var safeDistance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 200; for (var i = 0; i < collectibles.length; i++) { if (Math.abs(collectibles[i].x - x) < safeDistance) { return false; } } return true; } }; var ScoreManager = function ScoreManager() { var self = {}; // Initialize score and UI self.score = 0; self.scoreText = new Text2('0', { size: 160, fill: 0xFFFFFF, anchorX: 1, anchorY: 0.5 }); self.coinIcon = LK.getAsset('coin', { anchorX: 0, anchorY: 0.5, scaleX: 2, scaleY: 2 }); // Initialize container self.container = new Container(); self.container.addChild(self.scoreText); self.container.addChild(self.coinIcon); // Position container self.container.x = GAME_WIDTH - 100; self.container.y = 100; // Position elements self.scoreText.x = -100; self.scoreText.y = -100; self.coinIcon.x = 0; self.coinIcon.y = 0; // Getter and setter for score self.getScore = function () { return self.score; }; self.setScore = function (newScore) { self.score = newScore; self.scoreText.setText(newScore); // Adjust position based on digit count if (newScore >= 10 && newScore < 100) { self.scoreText.x = -200; } else if (newScore >= 100 && newScore < 1000) { self.scoreText.x = -300; } else if (newScore >= 1000) { self.scoreText.x = -400; } else { self.scoreText.x = -100; } }; // Add score and display popup self.addScore = function (amount, x, y) { self.setScore(self.score + amount); LK.setScore(self.score); // Create score popup if position is provided if (x !== undefined && y !== undefined) { var popup = new ScorePopup(x, y - 30, amount); game.addChild(popup); } }; return self; }; /**** * Game Variables ****/ // Containers var backgroundContainer = game.addChild(new Container()); var midgroundContainer = game.addChild(new Container()); var foregroundContainer = game.addChild(new Container()); var scoreManager = new ScoreManager(); game.addChild(scoreManager.container); var scoreContainer = game.addChild(new Container()); // Game state var gameStarted = false; var titleScreen; var playButton; // Platform management var platforms = []; var platformSpawnCounter = 0; var platformsUntilNextChange = 5; var lowPlatformHeight = GAME_HEIGHT / 1.5 + PLAYER_PLATFORM_OFFSET; var highPlatformHeight = lowPlatformHeight - 600; var currentPlatformHeight = lowPlatformHeight; var lastPlatformHeight = lowPlatformHeight; var lastPlatformX = 0; // Touch controls var touchStartX = 0; var touchStartY = 0; var touchEndX = 0; var touchEndY = 0; var lastMoveY = 0; var lastJumpTime = 0; // Game objects var jars = []; var coins = []; var collectibles = []; var jarSpawnCounter = 0; var jarSpawnInterval = 20; var chestSpawnCounter = 0; var chestSpawnInterval = 100; var enemies = []; var enemySpawnInterval = 100; var goblinSpawnCounter = 0; var goblinSpawnInterval = 100; var eyeballSpawnCounter = 150; var eyeballSpawnInterval = 500; var particleSystem; var heartContainer = new HeartContainer(); var player; // Background elements var bg1 = backgroundContainer.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 1 })); var bg2 = backgroundContainer.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 1 })); bg1.y = GAME_HEIGHT; bg2.y = GAME_HEIGHT; bg2.x = GAME_WIDTH; // Midground elements var mg1 = midgroundContainer.addChild(LK.getAsset('midground', { anchorX: 0, anchorY: 1 })); var mg2 = midgroundContainer.addChild(LK.getAsset('midground', { anchorX: 0, anchorY: 1 })); mg1.y = GAME_HEIGHT; mg2.y = GAME_HEIGHT; mg2.x = GAME_WIDTH; // Foreground elements var fg1 = foregroundContainer.addChild(LK.getAsset('foreground', { anchorX: 0, anchorY: 1 })); var fg2 = foregroundContainer.addChild(LK.getAsset('foreground', { anchorX: 0, anchorY: 1 })); fg1.y = GAME_HEIGHT * 1.25; fg2.y = GAME_HEIGHT * 1.25; fg1.x = 0; fg2.x = GAME_WIDTH; /**** * Game Functions ****/ // Create the title screen function createTitleScreen() { titleScreen = new Container(); game.addChild(titleScreen); // Add title image with fade-in var titleImage = titleScreen.attachAsset('title', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); titleImage.x = GAME_WIDTH / 2; titleImage.y = GAME_HEIGHT / 2.7; // Fade in title tween(titleImage, { alpha: 1 }, { duration: 1000, easing: tween.easeIn }); // Add play button playButton = titleScreen.attachAsset('playbutton', { anchorX: 0.5, anchorY: 0.5 }); playButton.x = GAME_WIDTH / 2; playButton.y = GAME_HEIGHT / 1.3; // Add flashing animation function flashPlayButton() { tween(playButton, { alpha: 0 }, { duration: 250, easing: tween.linear, onFinish: function onFinish() { tween(playButton, { alpha: 1 }, { duration: 250, easing: tween.linear }); } }); } // Flash every 2 seconds LK.setInterval(flashPlayButton, 2000); // Initialize torch decorations initializeTorches(); } // Place torches in the scene function initializeTorches() { // Create torches for the two background sections var torch1 = new Torch(); torch1.x = 25; torch1.y = GAME_HEIGHT * 0.7; midgroundContainer.addChild(torch1); var torch2 = new Torch(); torch2.x = GAME_WIDTH + 25; torch2.y = GAME_HEIGHT * 0.7; midgroundContainer.addChild(torch2); } // Initialize game elements function initializeGame() { // Create player player = game.addChild(new Player()); player.x = GAME_WIDTH / 4.5; player.y = GAME_HEIGHT / 1.5; game.addChild(heartContainer); // Create initial platforms at the low level for (var i = 0; i < 5; i++) { var platform = new Platform(); if (i === 0) { // First platform centered on player platform.x = player.x; } else { // Position with slight overlap platform.x = lastPlatformX + PLATFORM_WIDTH - PLATFORM_OVERLAP; } platform.y = lowPlatformHeight; platforms.push(platform); game.addChild(platform); lastPlatformX = platform.x; } lastPlatformHeight = lowPlatformHeight; player.isOnGround = true; player.currentPlatform = platforms[0]; } // Start the game function startGame() { gameStarted = true; titleScreen.destroy(); initializeGame(); // Initialize particle system particleSystem = new ParticlePool(100); game.addChild(particleSystem); // Show health player.heartContainer.alpha = 1; player.heartVisibilityTimer = 120; // Play background music LK.playMusic('backgroundmusic1', { fade: { start: 0, end: 0.7, duration: 1000 } }); } // Update background layers function updateBackgrounds() { // Background layer (slowest) bg1.x -= PLATFORM_SPEED * 0.3; bg2.x -= PLATFORM_SPEED * 0.3; if (bg1.x <= -GAME_WIDTH) { bg1.x = bg2.x + GAME_WIDTH; } if (bg2.x <= -GAME_WIDTH) { bg2.x = bg1.x + GAME_WIDTH; } // Midground layer mg1.x -= PLATFORM_SPEED * 0.6; mg2.x -= PLATFORM_SPEED * 0.6; if (mg1.x <= -GAME_WIDTH) { mg1.x = mg2.x + GAME_WIDTH; } if (mg2.x <= -GAME_WIDTH) { mg2.x = mg1.x + GAME_WIDTH; } // Foreground layer (fastest) fg1.x -= PLATFORM_SPEED; fg2.x -= PLATFORM_SPEED; if (fg1.x <= -GAME_WIDTH) { fg1.x = fg2.x + GAME_WIDTH; } if (fg2.x <= -GAME_WIDTH) { fg2.x = fg1.x + GAME_WIDTH; } // Update torches for (var i = 0; i < midgroundContainer.children.length; i++) { var child = midgroundContainer.children[i]; if (child instanceof Torch) { child.update(); child.x -= PLATFORM_SPEED * 0.6; if (child.x <= -GAME_WIDTH) { child.x = child.x + GAME_WIDTH * 2; } } } } // Update and spawn platforms function updatePlatforms() { // Check if we need to spawn a new sequence of platforms var lastPlatform = platforms[platforms.length - 1]; if (lastPlatform && lastPlatform.x < GAME_WIDTH + 500) { // Time to spawn a new sequence if (platformsUntilNextChange <= 0) { // Switch height currentPlatformHeight = currentPlatformHeight === lowPlatformHeight ? highPlatformHeight : lowPlatformHeight; // Generate new random sequence length platformsUntilNextChange = Math.floor(Math.random() * (MAX_PLATFORMS_IN_SEQUENCE - MIN_PLATFORMS_IN_SEQUENCE + 1)) + MIN_PLATFORMS_IN_SEQUENCE; // Make ground sequences longer if (currentPlatformHeight === lowPlatformHeight) { platformsUntilNextChange += 5; } } // Spawn a single platform in the sequence var platform = new Platform(); platform.x = lastPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP); platform.y = currentPlatformHeight; platforms.push(platform); game.addChild(platform); // Decrement the counter for each platform added platformsUntilNextChange--; } // Update platforms for (var i = platforms.length - 1; i >= 0; i--) { platforms[i].update(); // Remove platforms that are destroyed if (platforms[i].destroyed) { platforms.splice(i, 1); } } } // Update and spawn collectibles function updateCollectibles() { // Jar spawning jarSpawnCounter++; if (jarSpawnCounter >= jarSpawnInterval) { var availablePlatforms = platforms.filter(function (p) { return p.x > GAME_WIDTH && p.x < GAME_WIDTH + 300; }); if (availablePlatforms.length > 0 && Math.random() < 0.3) { var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)]; // Only spawn if position is clear if (GameUtils.canSpawnAtPosition(platform.x)) { var jar = new Jar(); jar.x = platform.x; jar.y = platform.y - 130; jar.currentPlatform = platform; collectibles.push(jar); game.addChild(jar); } } jarSpawnCounter = 0; } // Treasure chest spawning chestSpawnCounter++; if (chestSpawnCounter >= chestSpawnInterval) { var availablePlatforms = platforms.filter(function (p) { return p.x > GAME_WIDTH && p.x < GAME_WIDTH + 300; }); if (availablePlatforms.length > 0 && Math.random() < 0.15) { var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)]; // Only spawn if position is clear if (GameUtils.canSpawnAtPosition(platform.x)) { var chest = new TreasureChest(); chest.x = platform.x; chest.y = platform.y - 130; chest.currentPlatform = platform; collectibles.push(chest); game.addChild(chest); } } chestSpawnCounter = 0; } // Update existing collectibles for (var i = collectibles.length - 1; i >= 0; i--) { var collectible = collectibles[i]; if (collectible.currentPlatform) { collectible.x = collectible.currentPlatform.x; } var attackBounds = player.getAttackBounds(); if (attackBounds) { var itemBounds = { left: collectible.x - 50, right: collectible.x + 50, top: collectible.y - 75, bottom: collectible.y + 75 }; if (GameUtils.checkCollision(attackBounds, itemBounds)) { collectible["break"](); collectibles.splice(i, 1); continue; } } if (collectible.x < -50) { collectible.destroy(); collectibles.splice(i, 1); } } // Update coins and other collectibles for (var i = coins.length - 1; i >= 0; i--) { coins[i].update(); if (coins[i].destroyed) { coins.splice(i, 1); } } // Update score popups for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child instanceof ScorePopup) { child.update(); } } } // Update and spawn enemies function updateEnemies() { // Goblin spawning goblinSpawnCounter++; if (goblinSpawnCounter >= goblinSpawnInterval) { var availablePlatforms = platforms.filter(function (p) { return p.x > GAME_WIDTH - 100 && p.x < GAME_WIDTH + 300; }); if (availablePlatforms.length > 0) { var enemy = new Enemy('goblin'); var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)]; enemy.x = platform.x; enemy.y = platform.y - ENEMY_PLATFORM_OFFSET; enemy.currentPlatform = platform; enemies.push(enemy); game.addChild(enemy); goblinSpawnInterval = Math.floor(Math.random() * 150) + 100; goblinSpawnCounter = 0; } else { // No valid platforms, reset counter but don't spawn goblinSpawnCounter = Math.max(0, goblinSpawnCounter - 20); } } // Eyeball spawning eyeballSpawnCounter++; if (eyeballSpawnCounter >= eyeballSpawnInterval) { var enemy = new Enemy('eyeball'); var randomHeight = Math.random() * 400 + 200; enemy.x = GAME_WIDTH + 100; enemy.y = highPlatformHeight - randomHeight; enemies.push(enemy); game.addChild(enemy); eyeballSpawnInterval = Math.floor(Math.random() * 300) + 150; eyeballSpawnCounter = 0; } // Update enemies and check collisions for (var j = enemies.length - 1; j >= 0; j--) { enemies[j].update(); // Skip if enemy is far behind player if (enemies[j].x < player.x - 100) { continue; } var playerBounds = player.getBounds(); var enemyBounds = enemies[j].getBounds(); var attackBounds = player.getAttackBounds(); // Check for attack collision first if (attackBounds && !enemies[j].isHit && !enemies[j].isDying) { if (enemies[j].x > player.x && GameUtils.checkCollision(attackBounds, enemyBounds)) { enemies[j].hit(); if (enemies[j].type === 'eyeball') { LK.getSound('eyeballhit').play(); } else { LK.getSound('enemyhit').play(); } continue; } } // Check for body collision second if (GameUtils.checkCollision(playerBounds, enemyBounds)) { if (!enemies[j].isHit && !enemies[j].isDying) { player.takeDamage(); } } } } // Create the initial title screen createTitleScreen(); /**** * Game Loop & Input Handlers ****/ // Main game update loop game.update = function () { // Always update backgrounds for visual effect updateBackgrounds(); // If game hasn't started, don't update gameplay if (!gameStarted) { return; } // Update game entities player.update(); if (particleSystem) { particleSystem.update(); } // Update game world updatePlatforms(); updateCollectibles(); updateEnemies(); }; // Handle touch/click events game.down = function (x, y, obj) { touchStartX = x; touchStartY = y; }; game.up = function (x, y, obj) { if (!gameStarted) { var buttonBounds = { left: playButton.x - playButton.width / 2, right: playButton.x + playButton.width / 2, top: playButton.y - playButton.height / 2, bottom: playButton.y + playButton.height / 2 }; if (x >= buttonBounds.left && x <= buttonBounds.right && y >= buttonBounds.top && y <= buttonBounds.bottom) { startGame(); } return; } touchEndX = x; touchEndY = y; var deltaY = touchStartY - touchEndY; var deltaX = Math.abs(touchStartX - touchEndX); var currentTime = Date.now(); if (deltaY > 50 && deltaY > deltaX) { if (currentTime - lastJumpTime > JUMP_COOLDOWN) { player.jump(); lastJumpTime = currentTime; } } else { // Only attack if it wasn't a jump attempt player.attack(); } }; game.move = function (x, y, obj) { if (!gameStarted) { return; } if (lastMoveY !== 0) { var deltaY = y - lastMoveY; var currentTime = Date.now(); if (deltaY < -MOVE_THRESHOLD && currentTime - lastJumpTime > JUMP_COOLDOWN) { player.jump(); lastJumpTime = currentTime; } } lastMoveY = y; };
===================================================================
--- original.js
+++ change.js
@@ -5,22 +5,20 @@
/****
* Classes
****/
+// Refactored classes - keeping original Container.expand pattern
var Coin = Container.expand(function (type) {
var self = Container.call(this);
+ // Set type and create sprite
self.type = type || 'coin';
- // Create sprite based on type
self.sprite = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5,
- tint: 0xFFFFFF // No tint for gems
+ tint: 0xFFFFFF
});
- self.velocityX = 0;
- self.velocityY = 0;
- self.collected = false;
- self.bounceCount = 0;
- self.maxBounces = 2;
+ // Initialize physics properties
+ CollectibleBehavior.initPhysics(self);
// Get value based on type
self.getValue = function () {
switch (self.type) {
case 'diamond':
@@ -32,10 +30,9 @@
default:
return 1;
}
};
- // Rest of the existing Coin code...
- // Make sure to use self.getValue() in the collect function
+ // Collection functionality
self.collect = function () {
if (!self.collected) {
self.collected = true;
var value = self.getValue();
@@ -43,186 +40,300 @@
LK.getSound('coincollect').play();
self.destroy();
}
};
+ // Standard update method using shared behavior
self.update = function () {
- if (self.collected) {
- return;
- }
- // Apply physics
- self.velocityY += 0.5; // gravity
- self.x += self.velocityX;
- self.y += self.velocityY;
- // Check for platform collision with bounce
- // Only bounce if we're moving downward and hit a platform
- if (self.velocityY > 0 && self.checkPlatformCollision()) {
- if (self.bounceCount < self.maxBounces) {
- LK.getSound('coinbounce').play();
- var impactSpeed = Math.abs(self.velocityY);
- self.velocityY = -(impactSpeed * 0.5); // Use half of impact speed
- self.velocityX *= 0.8;
- self.bounceCount++;
- } else {
- self.velocityY = 0;
- self.velocityX = -5; // Match platform speed
- }
- }
- // Check if off screen
- if (self.x < -50 || self.x > 2048 + 50 || self.y > 2732) {
- self.destroy();
- }
- // Player collection detection
- var playerBounds = player.getBounds();
- var coinBounds = {
- left: self.x - 25,
- right: self.x + 25,
- top: self.y - 25,
- bottom: self.y + 25
- };
- if (playerBounds.left < coinBounds.right && playerBounds.right > coinBounds.left && playerBounds.top < coinBounds.bottom && playerBounds.bottom > coinBounds.top) {
- self.collect();
- }
+ CollectibleBehavior.standardUpdate(self);
};
self.checkPlatformCollision = function () {
- for (var i = 0; i < platforms.length; i++) {
- var platform = platforms[i];
- if (Math.abs(self.y - (platform.y - 80)) < 10 && self.x > platform.x - 500 && self.x < platform.x + 500) {
- self.y = platform.y - 80;
- return true;
- }
- }
- return false;
+ return GameUtils.checkPlatformCollision(self, 80, true) != null;
};
return self;
});
+// Enemy class with refactored animation management
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
// Enemy properties
self.type = type || 'basic';
self.speed = 7;
self.isOnGround = true;
self.velocityY = 0;
self.currentPlatform = null;
- self.groundY = 2732 / 1.5; // Same as player ground Y
- self.hitAnimation = ['goblinhit1'];
- self.dieAnimation = ['goblindie1', 'goblindie2', 'goblindie3', 'goblindie4'];
+ self.groundY = GAME_HEIGHT / 1.5;
self.isHit = false;
self.isDying = false;
self.deathTimer = 0;
- self.throwBackSpeed = 15; // Initial backwards throw speed
- self.throwBackDistance = 0; // Track distance thrown
- self.maxThrowBack = 200; // Maximum throw back distance
- // Add bounding box properties
- self.hitboxWidth = 200; // Smaller than the 150px sprite width
- self.hitboxHeight = 260; // Adjusted for goblin height
- // Animation properties for goblin
- self.runAnimation = [];
- self.runFrame = 0;
- self.animationSpeed = 0.08;
- self.animationCounter = 0;
+ self.throwBackSpeed = 15;
+ self.throwBackDistance = 0;
+ self.maxThrowBack = 200;
+ // Hitbox properties
+ self.hitboxWidth = 200;
+ self.hitboxHeight = self.type === 'eyeball' ? 90 : 260;
+ // Animation properties
self.sprites = [];
- // Add eyeball-specific properties
- self.isFlying = type === 'eyeball';
- self.flyingHeight = 0;
- self.verticalSpeed = 2; // Speed of vertical adjustment
- self.maxVerticalSpeed = 4; // Maximum vertical speed
- self.homingDelay = 80; // Wait 60 frames before homing
- self.homingTimer = 0; // Track how long the eyeball has been alive
- // Add eyeball animations
- self.flyAnimation = ['eyefly1', 'eyefly2', 'eyefly3', 'eyefly4', 'eyefly5', 'eyefly6', 'eyefly7', 'eyefly8'];
- self.flyFrame = 0;
+ self.animationCounter = 0;
+ self.animationSpeed = 0.08;
// Initialize based on enemy type
if (self.type === 'eyeball') {
- // Eyeball animations
+ // Eyeball-specific properties
+ self.isFlying = true;
+ self.flyingHeight = 0;
+ self.verticalSpeed = 2;
+ self.maxVerticalSpeed = 4;
+ self.homingDelay = 80;
+ self.homingTimer = 0;
+ self.flyFrame = 0;
+ // Animation frames
self.flyAnimation = ['eyefly1', 'eyefly2', 'eyefly3', 'eyefly4', 'eyefly5', 'eyefly6', 'eyefly7', 'eyefly8'];
self.hitAnimation = ['eyedie1', 'eyedie2'];
self.dieAnimation = ['eyedie3', 'eyedie4', 'eyedie5'];
+ // Initialize animation sprites
+ self.initEyeballSprites();
} else if (self.type === 'goblin') {
- // Goblin animations
+ // Goblin-specific properties
+ self.runFrame = 0;
+ // Animation frames
self.runAnimation = ['goblinrun1', 'goblinrun2', 'goblinrun3', 'goblinrun4', 'goblinrun5', 'goblinrun6', 'goblinrun7', 'goblinrun8'];
self.hitAnimation = ['goblinhit1'];
self.dieAnimation = ['goblindie1', 'goblindie2', 'goblindie3', 'goblindie4'];
+ // Initialize animation sprites
+ self.initGoblinSprites();
+ } else {
+ // Basic enemy
+ var enemyGraphics = self.attachAsset('enemy', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
}
- // Initialize based on enemy type
- if (self.type === 'goblin') {
- // Setup all animations
- self.runAnimation = ['goblinrun1', 'goblinrun2', 'goblinrun3', 'goblinrun4', 'goblinrun5', 'goblinrun6', 'goblinrun7', 'goblinrun8'];
- // Pre-attach all animation frames
- // Run animation
- for (var i = 0; i < self.runAnimation.length; i++) {
- var sprite = self.attachAsset(self.runAnimation[i], {
+ // Initialize animation sprites for eyeball
+ self.initEyeballSprites = function () {
+ // Add fly animations
+ for (var i = 0; i < self.flyAnimation.length; i++) {
+ var sprite = self.attachAsset(self.flyAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
- // Hit animation
+ // Add hit animations
for (var i = 0; i < self.hitAnimation.length; i++) {
var sprite = self.attachAsset(self.hitAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = 0;
self.sprites.push(sprite);
}
- // Die animation
+ // Add die animations
for (var i = 0; i < self.dieAnimation.length; i++) {
var sprite = self.attachAsset(self.dieAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = 0;
self.sprites.push(sprite);
}
- } else if (self.type === 'eyeball') {
- // First add fly animations (8 frames)
- for (var i = 0; i < self.flyAnimation.length; i++) {
- var sprite = self.attachAsset(self.flyAnimation[i], {
+ };
+ // Initialize animation sprites for goblin
+ self.initGoblinSprites = function () {
+ // Add run animations
+ for (var i = 0; i < self.runAnimation.length; i++) {
+ var sprite = self.attachAsset(self.runAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
- // Then hit animations (2 frames)
+ // Add hit animations
for (var i = 0; i < self.hitAnimation.length; i++) {
var sprite = self.attachAsset(self.hitAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = 0;
self.sprites.push(sprite);
}
- // Then die animations (3 frames)
+ // Add die animations
for (var i = 0; i < self.dieAnimation.length; i++) {
var sprite = self.attachAsset(self.dieAnimation[i], {
anchorX: 0.5,
anchorY: 0.5
});
sprite.alpha = 0;
self.sprites.push(sprite);
}
- // Adjust hitbox for eyeball
- self.hitboxWidth = 200;
- self.hitboxHeight = 90;
- // Flying enemies don't use ground physics
- self.isOnGround = false;
- } else {
- // Basic enemy (original version)
- var enemyGraphics = self.attachAsset('enemy', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- }
+ };
+ // Hide all animation sprites
+ self.hideAllSprites = function () {
+ for (var i = 0; i < self.sprites.length; i++) {
+ self.sprites[i].alpha = 0;
+ }
+ };
+ // Update eyeball hit/die animation
+ self.updateEyeballDamageState = function () {
+ if (self.isHit) {
+ var hitOffset = self.flyAnimation.length;
+ self.throwBackDistance += Math.abs(self.throwBackSpeed);
+ self.x += self.throwBackSpeed;
+ self.throwBackSpeed *= 0.95;
+ // Show hit animation
+ var hitFrame = Math.floor(self.hitTimer / 100) % 2;
+ self.sprites[hitOffset + hitFrame].alpha = 1;
+ self.hitTimer--;
+ if (self.hitTimer <= 0) {
+ self.isHit = false;
+ self.isDying = true;
+ self.deathTimer = 180;
+ self.deathFrame = 0;
+ }
+ } else if (self.isDying) {
+ var dieOffset = self.flyAnimation.length + self.hitAnimation.length;
+ // Always check for ground collision during fall
+ if (!self.isOnGround) {
+ self.velocityY += 0.5;
+ self.y += self.velocityY;
+ }
+ // Progress through death frames
+ if (self.deathTimer > 120) {
+ self.sprites[dieOffset].alpha = 1; // eyedie3
+ } else if (self.deathTimer > 60) {
+ self.sprites[dieOffset + 1].alpha = 1; // eyedie4
+ } else {
+ self.sprites[dieOffset + 2].alpha = 1; // eyedie5
+ }
+ self.deathTimer--;
+ if (self.deathTimer <= 0) {
+ self.alpha -= 0.05;
+ if (self.alpha <= 0) {
+ self.destroy();
+ }
+ }
+ }
+ };
+ // Update eyeball normal movement
+ self.updateEyeballNormalState = function () {
+ self.x -= self.speed;
+ // Only start homing after delay
+ if (self.homingTimer >= self.homingDelay) {
+ // Home toward player
+ var deltaY = player.y - self.y;
+ self.velocityY += deltaY > 0 ? 0.2 : -0.2;
+ self.velocityY = Math.max(-self.maxVerticalSpeed, Math.min(self.maxVerticalSpeed, self.velocityY));
+ } else {
+ // Before homing, maintain height with slight wave motion
+ self.velocityY = Math.sin(self.homingTimer * 0.05) * 2;
+ self.homingTimer++;
+ }
+ self.y += self.velocityY;
+ // Animate
+ self.animationCounter += self.animationSpeed;
+ if (self.animationCounter >= 1) {
+ self.animationCounter = 0;
+ self.flyFrame = (self.flyFrame + 1) % self.flyAnimation.length;
+ }
+ self.sprites[self.flyFrame].alpha = 1;
+ };
+ // Update goblin hit animation
+ self.updateGoblinHitState = function () {
+ // Handle throw back motion
+ self.x += self.throwBackSpeed;
+ self.throwBackDistance += Math.abs(self.throwBackSpeed);
+ self.throwBackSpeed *= 0.95;
+ // Show hit animation
+ var hitOffset = self.runAnimation.length;
+ self.sprites[hitOffset].alpha = 1;
+ // Decrease hit timer
+ self.hitTimer--;
+ // Once hit timer expires, start death animation
+ if (self.hitTimer <= 0) {
+ self.isHit = false;
+ self.isDying = true;
+ self.deathTimer = 60;
+ self.deathFrame = 0;
+ }
+ };
+ // Update goblin dying animation
+ self.updateGoblinDyingState = function () {
+ // Continue throw back during death
+ self.x += self.throwBackSpeed;
+ // After halfway through death animation, match platform speed
+ if (self.deathFrame >= 2) {
+ self.x -= PLATFORM_SPEED;
+ }
+ self.throwBackSpeed *= 0.95;
+ // Handle death animation
+ var dieOffset = self.runAnimation.length + self.hitAnimation.length;
+ // Progress frame every 15 frames
+ if (self.deathTimer % 15 === 0 && self.deathFrame < self.dieAnimation.length - 1) {
+ self.deathFrame++;
+ }
+ self.sprites[dieOffset + self.deathFrame].alpha = 1;
+ // Count down death timer
+ self.deathTimer--;
+ // After timer expires, fade out
+ if (self.deathTimer <= 0) {
+ self.alpha -= 0.1;
+ if (self.alpha <= 0) {
+ self.destroy();
+ }
+ }
+ };
+ // Update goblin normal movement
+ self.updateGoblinNormalState = function () {
+ // Move left
+ self.x -= self.speed;
+ // Platform and gravity code
+ if (!self.isOnGround) {
+ self.velocityY += 0.7;
+ self.y += self.velocityY;
+ self.checkPlatformCollision();
+ }
+ // Ensure goblin stays on platform
+ if (self.currentPlatform) {
+ var stillOnPlatform = self.x >= self.currentPlatform.x - PLATFORM_HALF_WIDTH && self.x <= self.currentPlatform.x + PLATFORM_HALF_WIDTH;
+ if (!stillOnPlatform) {
+ var foundAnotherPlatform = false;
+ // Check for another platform
+ for (var i = 0; i < platforms.length; i++) {
+ var otherPlatform = platforms[i];
+ if (otherPlatform === self.currentPlatform) {
+ continue;
+ }
+ if (self.x >= otherPlatform.x - PLATFORM_HALF_WIDTH && self.x <= otherPlatform.x + PLATFORM_HALF_WIDTH && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
+ self.currentPlatform = otherPlatform;
+ foundAnotherPlatform = true;
+ break;
+ }
+ }
+ // Start falling if no other platform found
+ if (!foundAnotherPlatform) {
+ self.isOnGround = false;
+ self.currentPlatform = null;
+ if (self.velocityY === 0) {
+ self.velocityY = 0.1;
+ }
+ }
+ }
+ }
+ // Animate running
+ self.animationCounter += self.animationSpeed;
+ if (self.animationCounter >= 1) {
+ self.animationCounter = 0;
+ self.runFrame = (self.runFrame + 1) % self.runAnimation.length;
+ }
+ self.sprites[self.runFrame].alpha = 1;
+ };
+ // Check platform collision
self.checkPlatformCollision = function () {
var onAnyPlatform = false;
- var platformHalfWidth = 500;
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
- var leftEdge = platform.x - platformHalfWidth;
- var rightEdge = platform.x + platformHalfWidth;
- // First check if we're on a platform (exact position check)
+ var leftEdge = platform.x - PLATFORM_HALF_WIDTH;
+ var rightEdge = platform.x + PLATFORM_HALF_WIDTH;
+ // Check if we're on a platform (exact position check)
if (self.x >= leftEdge && self.x <= rightEdge) {
if (Math.abs(self.y - (platform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
onAnyPlatform = true;
self.currentPlatform = platform;
@@ -230,13 +341,10 @@
self.isOnGround = true;
self.velocityY = 0;
return true;
}
- // NEW CODE: Check for passing through platform during fall
- // This detects if the enemy will cross the platform in the next frame
- if (self.velocityY > 0 &&
- // Must be falling down
- self.y < platform.y - ENEMY_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - ENEMY_PLATFORM_OFFSET) {
+ // Check for passing through platform during fall
+ if (self.velocityY > 0 && self.y < platform.y - ENEMY_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - ENEMY_PLATFORM_OFFSET) {
self.y = platform.y - ENEMY_PLATFORM_OFFSET;
self.velocityY = 0;
self.isOnGround = true;
self.currentPlatform = platform;
@@ -249,225 +357,75 @@
self.currentPlatform = null;
}
return false;
};
- // Update collision check method
+ // Get collision bounds
self.getBounds = function () {
return {
left: self.x - self.hitboxWidth / 2,
right: self.x + self.hitboxWidth / 2,
top: self.y - self.hitboxHeight / 2,
bottom: self.y + self.hitboxHeight / 2
};
};
+ // Hit handling
self.hit = function () {
if (!self.isHit && !self.isDying) {
self.isHit = true;
- self.throwBackSpeed = 25; // Increased from 25 for more distance
+ self.throwBackSpeed = 25;
self.throwBackDistance = 0;
- self.hitTimer = 35; // Added hit timer for longer hit frame
- // Add particle effect here
- var particleOffset = self.type === 'eyeball' ? 175 : 250; // smaller offset for eyeballs
+ self.hitTimer = 35;
+ // Add particle effect
+ var particleOffset = self.type === 'eyeball' ? 175 : 250;
particleSystem.emitFromHit(self.x + particleOffset, self.y, player.x);
}
};
+ // Main update method
self.update = function () {
// Hide all sprites first
- for (var i = 0; i < self.sprites.length; i++) {
- self.sprites[i].alpha = 0;
- }
- if (self.type === 'eyeball' && (self.isHit || self.isDying)) {
- // First handle the hit state
- if (self.isHit) {
- var hitOffset = self.flyAnimation.length;
- self.throwBackDistance += Math.abs(self.throwBackSpeed);
- self.x += self.throwBackSpeed;
- self.throwBackSpeed *= 0.95;
- // Show hit animation (eyedie1, eyedie2)
- var hitFrame = Math.floor(self.hitTimer / 100) % 2;
- self.sprites[hitOffset + hitFrame].alpha = 1;
- self.hitTimer--;
- if (self.hitTimer <= 0) {
- self.isHit = false;
- self.isDying = true;
- self.deathTimer = 180; // 3 seconds for death sequence
- self.deathFrame = 0; // Start at first death frame
- }
- }
- // Then handle the dying state
- else if (self.isDying) {
- var dieOffset = self.flyAnimation.length + self.hitAnimation.length;
- // Always check for ground collision during fall
- if (!self.isOnGround) {
- self.velocityY += 0.5;
- self.y += self.velocityY;
- }
- //self.x -= 5; // Match platform speed
- // Progress through death frames
- if (self.deathTimer > 120) {
- self.sprites[dieOffset].alpha = 1; // eyedie3
- } else if (self.deathTimer > 60) {
- self.sprites[dieOffset + 1].alpha = 1; // eyedie4
- } else {
- self.sprites[dieOffset + 2].alpha = 1; // eyedie5
- }
- self.deathTimer--;
- if (self.deathTimer <= 0) {
- self.alpha -= 0.05;
- if (self.alpha <= 0) {
- self.destroy();
- }
- }
- }
- return; // Skip regular animation updates
- } else if (self.type === 'eyeball') {
- self.x -= self.speed;
- // Only start homing after delay
- if (self.homingTimer >= self.homingDelay) {
- // Home toward player
- var deltaY = player.y - self.y;
- self.velocityY += deltaY > 0 ? 0.2 : -0.2;
- self.velocityY = Math.max(-self.maxVerticalSpeed, Math.min(self.maxVerticalSpeed, self.velocityY));
+ self.hideAllSprites();
+ // Handle different enemy types and states
+ if (self.type === 'eyeball') {
+ if (self.isHit || self.isDying) {
+ self.updateEyeballDamageState();
} else {
- // Before homing, just maintain height with slight wave motion
- self.velocityY = Math.sin(self.homingTimer * 0.05) * 2;
- self.homingTimer++;
+ self.updateEyeballNormalState();
}
- self.y += self.velocityY;
- // Animate
- self.animationCounter += self.animationSpeed;
- if (self.animationCounter >= 1) {
- self.animationCounter = 0;
- self.flyFrame = (self.flyFrame + 1) % self.flyAnimation.length;
- }
- self.sprites[self.flyFrame].alpha = 1;
- // Check if off screen
- if (self.x < -50 || self.y > 2732) {
- self.destroy();
- }
} else {
+ // Goblin logic
if (self.isHit) {
- // Handle throw back motion
- self.x += self.throwBackSpeed;
- self.throwBackDistance += Math.abs(self.throwBackSpeed);
- self.throwBackSpeed *= 0.95; // Slower decay than 0.9
- // Show hit animation
- var hitOffset = self.runAnimation.length;
- self.sprites[hitOffset].alpha = 1;
- // Decrease hit timer
- self.hitTimer--;
- // Once hit timer expires, start death animation
- if (self.hitTimer <= 0) {
- self.isHit = false;
- self.isDying = true;
- self.deathTimer = 60; // Increased to 60 frames
- self.deathFrame = 0; // Explicitly track which frame we're on
- }
+ self.updateGoblinHitState();
} else if (self.isDying) {
- // Continue throw back during death
- self.x += self.throwBackSpeed;
- // After halfway through death animation, match platform speed
- if (self.deathFrame >= 2) {
- // Since we have 4 frames, 2 is halfway
- self.x -= 5; // Platform speed is defined as 5
- }
- self.throwBackSpeed *= 0.95;
- // Removed vertical movement code
- // Handle death animation
- var dieOffset = self.runAnimation.length + self.hitAnimation.length;
- // Progress frame every 15 frames (60/4 frames = 15)
- if (self.deathTimer % 15 === 0 && self.deathFrame < self.dieAnimation.length - 1) {
- self.deathFrame++;
- }
- var dieOffset = self.runAnimation.length + self.hitAnimation.length;
- self.sprites[dieOffset + self.deathFrame].alpha = 1;
- // Count down death timer
- self.deathTimer--;
- // After timer expires, fade out
- if (self.deathTimer <= 0) {
- self.alpha -= 0.1;
- if (self.alpha <= 0) {
- self.destroy();
- }
- }
+ self.updateGoblinDyingState();
} else {
- // Original movement and animation code
- // Move left
- self.x -= self.speed;
- // Original platform and gravity code
- if (!self.isOnGround) {
- self.velocityY += 0.7;
- self.y += self.velocityY;
- if (!self.checkPlatformCollision()) {
- // Let them fall if not on platform
- }
- }
- if (self.currentPlatform) {
- var platformHalfWidth = 500; // Match the existing platform width constant
- var stillOnPlatform = self.x >= self.currentPlatform.x - platformHalfWidth && self.x <= self.currentPlatform.x + platformHalfWidth;
- if (!stillOnPlatform) {
- var foundAnotherPlatform = false;
- // Check if there's another platform we might have moved to
- for (var i = 0; i < platforms.length; i++) {
- var otherPlatform = platforms[i];
- if (otherPlatform === self.currentPlatform) {
- continue;
- }
- if (self.x >= otherPlatform.x - platformHalfWidth && self.x <= otherPlatform.x + platformHalfWidth && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
- // Found another platform at the same height
- self.currentPlatform = otherPlatform;
- foundAnotherPlatform = true;
- break;
- }
- }
- // If no other platform found, start falling
- if (!foundAnotherPlatform) {
- self.isOnGround = false;
- self.currentPlatform = null;
- // Start falling with initial velocity
- if (self.velocityY === 0) {
- self.velocityY = 0.1;
- }
- }
- }
- }
- // Handle run animation
- if (self.type === 'goblin') {
- self.animationCounter += self.animationSpeed;
- if (self.animationCounter >= 1) {
- self.animationCounter = 0;
- self.runFrame = (self.runFrame + 1) % self.runAnimation.length;
- }
- self.sprites[self.runFrame].alpha = 1;
- }
+ self.updateGoblinNormalState();
}
- // Destroy if off screen
- if (self.x < -50 || self.y > 2732) {
- self.destroy();
- }
}
+ // Destroy if off screen
+ if (self.x < -50 || self.y > GAME_HEIGHT) {
+ self.destroy();
+ }
};
+ return self;
});
var HealthPotion = Container.expand(function () {
var self = Container.call(this);
// Create sprite
self.sprite = self.attachAsset('healthpotion', {
anchorX: 0.5,
- anchorY: 0.5
+ anchorY: 0.8
});
- self.velocityX = 0;
- self.velocityY = 0;
- self.collected = false;
- self.bounceCount = 0;
- self.maxBounces = 2;
+ // Initialize physics properties
+ CollectibleBehavior.initPhysics(self);
+ // Collection functionality
self.collect = function () {
if (!self.collected && player.currentHealth < player.maxHealth) {
self.collected = true;
player.currentHealth++;
player.heartContainer.updateHealth(player.currentHealth);
player.heartContainer.alpha = 1;
player.heartVisibilityTimer = player.heartVisibilityDuration;
- // Create heart popup (similar to score popup)
+ // Create heart popup
var popup = new Text2('ā¤ļø', {
size: 80,
fill: 0xFF0000,
anchorX: 0.5,
@@ -491,54 +449,14 @@
LK.getSound('potion').play();
self.destroy();
}
};
+ // Standard update using shared behavior
self.update = function () {
- if (self.collected) {
- return;
- }
- // Apply physics
- self.velocityY += 0.5; // gravity
- self.x += self.velocityX;
- self.y += self.velocityY;
- // Check for platform collision with bounce
- if (self.velocityY > 0 && self.checkPlatformCollision()) {
- if (self.bounceCount < self.maxBounces) {
- LK.getSound('coinbounce').play();
- var impactSpeed = Math.abs(self.velocityY);
- self.velocityY = -(impactSpeed * 0.5);
- self.velocityX *= 0.8;
- self.bounceCount++;
- } else {
- self.velocityY = 0;
- self.velocityX = -5; // Match platform speed
- }
- }
- // Check if off screen
- if (self.x < -50 || self.x > 2048 + 50 || self.y > 2732) {
- self.destroy();
- }
- // Player collection detection
- var playerBounds = player.getBounds();
- var potionBounds = {
- left: self.x - 25,
- right: self.x + 25,
- top: self.y - 25,
- bottom: self.y + 25
- };
- if (playerBounds.left < potionBounds.right && playerBounds.right > potionBounds.left && playerBounds.top < potionBounds.bottom && playerBounds.bottom > potionBounds.top) {
- self.collect();
- }
+ CollectibleBehavior.standardUpdate(self);
};
self.checkPlatformCollision = function () {
- for (var i = 0; i < platforms.length; i++) {
- var platform = platforms[i];
- if (Math.abs(self.y - (platform.y - 80)) < 10 && self.x > platform.x - 500 && self.x < platform.x + 500) {
- self.y = platform.y - 80;
- return true;
- }
- }
- return false;
+ return GameUtils.checkPlatformCollision(self, 80, true) != null;
};
return self;
});
var HeartContainer = Container.expand(function () {
@@ -567,13 +485,13 @@
// Update heart display
for (var i = 0; i < self.maxHealth; i++) {
if (i < newHealth) {
// Full heart
- self.hearts[i].tint = 0xFF0000; // Red
+ self.hearts[i].tint = 0xFF0000;
self.hearts[i].alpha = 1;
} else {
// Empty heart
- self.hearts[i].tint = 0x000000; // Black
+ self.hearts[i].tint = 0x000000;
self.hearts[i].alpha = 0.5;
}
}
};
@@ -584,95 +502,57 @@
// Attach jar sprite
self.sprite = self.attachAsset('jar', {
anchorX: 0.5,
anchorY: 0.5,
- tint: 0xC0C0C0 // Change tint to an even lighter grey
+ tint: 0xC0C0C0
});
- self.isBreaking = false;
- self.currentPlatform = null;
+ // Initialize as breakable
+ BreakableBehavior.initBreakable(self);
+ // Break functionality
self["break"] = function () {
- if (self.isBreaking) {
- return;
- }
- self.isBreaking = true;
- // Spawn jar pieces (keep existing piece code)
- for (var i = 1; i <= 4; i++) {
- var piece = new JarPiece(i);
- piece.x = self.x;
- piece.y = self.y;
- piece.velocityX = Math.random() * 10 - 5;
- piece.velocityY = -(Math.random() * 10 + 5);
- piece.rotationSpeed = Math.random() * 0.2 - 0.1;
- game.addChild(piece);
- }
- // Modified coin spawning with more forward motion
- var coinCount = Math.floor(Math.random() * 8) + 1;
- for (var i = 0; i < coinCount; i++) {
- var coin = new Coin();
- coin.x = self.x;
- coin.y = self.y;
- // More forward motion and higher initial jump
- coin.velocityX = Math.random() * 8 + 4; // Minimum 4, maximum 12 right velocity
- coin.velocityY = -(Math.random() * 10 + 12); // Higher jump
- game.addChild(coin);
- coins.push(coin);
- }
- LK.getSound('jarbreak').play();
- self.destroy();
+ BreakableBehavior.standardBreak(self, JarPiece, 4, function (jar) {
+ // Spawn health potion with low chance
+ if (Math.random() < 0.05) {
+ var potion = new HealthPotion();
+ potion.x = jar.x;
+ potion.y = jar.y;
+ potion.velocityX = GameUtils.randomRange(4, 12);
+ potion.velocityY = -GameUtils.randomRange(12, 22);
+ game.addChild(potion);
+ coins.push(potion);
+ }
+ // Spawn coins
+ var coinCount = Math.floor(GameUtils.randomRange(1, 9));
+ for (var i = 0; i < coinCount; i++) {
+ var coin = new Coin();
+ coin.x = jar.x;
+ coin.y = jar.y;
+ coin.velocityX = GameUtils.randomRange(4, 12);
+ coin.velocityY = -GameUtils.randomRange(12, 22);
+ game.addChild(coin);
+ coins.push(coin);
+ }
+ LK.getSound('jarbreak').play();
+ });
};
return self;
});
var JarPiece = Container.expand(function (pieceNum) {
var self = Container.call(this);
+ // Attach piece sprite
self.sprite = self.attachAsset('jarpiece' + pieceNum, {
anchorX: 0.5,
anchorY: 0.5,
- tint: 0xC0C0C0 // Apply grey tint
+ tint: 0xC0C0C0
});
- self.velocityX = 0;
- self.velocityY = 0;
- self.rotationSpeed = 0;
- self.fadeSpeed = 0.02;
- self.bounceCount = 0;
- self.maxBounces = 2; // Number of bounces before stopping
+ // Initialize as piece
+ PieceBehavior.initPiece(self);
+ // Update method
self.update = function () {
- // Apply physics
- self.velocityY += 0.5; // gravity
- self.x += self.velocityX;
- self.y += self.velocityY;
- self.rotation += self.rotationSpeed;
- // Check for platform collision with bounce
- if (self.checkPlatformCollision()) {
- if (self.bounceCount < self.maxBounces) {
- // Bounce with reduced velocity
- self.velocityY = -(self.velocityY * 0.4); // 40% of original velocity
- self.velocityX *= 0.8; // Reduce horizontal speed
- self.bounceCount++;
- } else {
- // Stop moving after max bounces
- self.velocityY = 0;
- self.velocityX = -5; // Match platform speed
- // Start fading
- self.alpha -= self.fadeSpeed;
- if (self.alpha <= 0) {
- self.destroy();
- }
- }
- }
- // Destroy if off screen
- if (self.x < -50 || self.y > 2732) {
- self.destroy();
- }
+ PieceBehavior.standardUpdate(self);
};
self.checkPlatformCollision = function () {
- for (var i = 0; i < platforms.length; i++) {
- var platform = platforms[i];
- if (Math.abs(self.y - (platform.y - 80)) < 10 && self.x > platform.x - 500 && self.x < platform.x + 500) {
- self.y = platform.y - 80;
- return true;
- }
- }
- return false;
+ return GameUtils.checkPlatformCollision(self, 80, true) != null;
};
return self;
});
var ParticlePool = Container.expand(function (maxParticles) {
@@ -684,10 +564,9 @@
var particle = self.attachAsset('pixel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
- // 4% of original size (4px from 100px)
- scaleY: 0.2 // 4% of original size (4px from 100px)
+ scaleY: 0.2
});
particle.alpha = 0;
particle.velocityX = 0;
particle.velocityY = 0;
@@ -707,10 +586,10 @@
particle.x = x;
particle.y = y;
particle.alpha = 1;
particle.tint = self.redTints[Math.floor(Math.random() * self.redTints.length)];
- // Set scale instead of width/height
- var particleSize = Math.random() * 0.2 + 0.2; // 2-4% of original size
+ // Set scale
+ var particleSize = Math.random() * 0.2 + 0.2;
particle.scaleX = particleSize;
particle.scaleY = particleSize;
var angle = Math.random() * Math.PI / 2 - Math.PI / 4;
var speed = Math.random() * 5 + 10;
@@ -741,107 +620,126 @@
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
- self.speed = 5;
+ self.speed = PLATFORM_SPEED;
self.passed = false;
self.update = function () {
self.x -= self.speed;
if (self.x < -500) {
- // Increased value to ensure platforms are fully off-screen
self.destroy();
}
};
+ return self;
});
-//<Assets used in the game will automatically appear here>
+// Player class with refactored animation management
var Player = Container.expand(function () {
var self = Container.call(this);
// Animation properties
self.runAnimation = ['playerrun1', 'playerrun2', 'playerrun3', 'playerrun4', 'playerrun5', 'playerrun6'];
self.jumpAnimation = ['playerjump1', 'playerjump2', 'playerjump3'];
self.attackAnimation = ['playerattack1', 'playerattack2', 'playerattack3', 'playerattack4', 'playerattack5'];
+ // Animation states
self.isAttacking = false;
self.attackFrame = 0;
self.runFrame = 0;
self.animationSpeed = 0.08;
- self.attackAnimationSpeed = 0.15; // Higher number = faster animation
+ self.attackAnimationSpeed = 0.15;
self.animationCounter = 0;
self.sprites = [];
- self.groundY = 2732 * 0.9; // Move ground much lower to allow falling from all platforms
- // Add bounding box properties
- self.hitboxWidth = 150; // Smaller than the 600px animation frames
- self.hitboxHeight = 300; // Adjusted for player height
- self.attackHitboxWidth = 200; // Width of attack hitbox
- self.attackHitboxHeight = 400; // Height of attack hitbox
- self.attackHitboxOffset = 50; // How far in front of player the attack hitbox extends
+ // Physics properties
+ self.groundY = GAME_HEIGHT * 0.9;
+ self.hitboxWidth = 150;
+ self.hitboxHeight = 300;
+ self.attackHitboxWidth = 200;
+ self.attackHitboxHeight = 400;
+ self.attackHitboxOffset = 50;
// Platform collision properties
self.isOnGround = true;
self.currentPlatform = null;
+ // Health properties
self.heartContainer = heartContainer;
self.maxHealth = 3;
self.currentHealth = 3;
self.isInvulnerable = false;
- self.invulnerabilityDuration = 90; // Frames of invulnerability after hit
+ self.invulnerabilityDuration = 90;
self.invulnerabilityTimer = 0;
- self.heartVisibilityTimer = 0; // New timer for heart visibility
- self.heartVisibilityDuration = 120; // Hearts visible for 120 frames (adjust as needed)/ Pre-attach all animation frames but make only the first one visible
- // First add run animation sprites
- for (var i = 0; i < self.runAnimation.length; i++) {
- var sprite = self.attachAsset(self.runAnimation[i], {
- anchorX: 0.5,
- anchorY: 0.5
- });
- // Set all frames except the first to be invisible
- sprite.alpha = i === 0 ? 1 : 0;
- self.sprites.push(sprite);
- }
- // Then add jump animation sprites
- for (var i = 0; i < self.jumpAnimation.length; i++) {
- var sprite = self.attachAsset(self.jumpAnimation[i], {
- anchorX: 0.5,
- anchorY: 0.5
- });
- // Set all jump frames to invisible initially
- sprite.alpha = 0;
- self.sprites.push(sprite);
- }
- for (var i = 0; i < self.attackAnimation.length; i++) {
- var sprite = self.attachAsset(self.attackAnimation[i], {
- anchorX: 0.5,
- anchorY: 0.5
- });
- sprite.alpha = 0;
- self.sprites.push(sprite);
- }
+ self.heartVisibilityTimer = 0;
+ self.heartVisibilityDuration = 120;
// Movement properties
self.speed = 5;
- self.jumpHeight = 40; // Keeping the original jump height
+ self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
- self.jumpState = "none"; // Added to track jump animation phases
+ self.jumpState = "none";
self.jumpStartTime = 0;
- // Add method to get actual collision bounds
+ // Initialize animation sprites
+ self.initAnimations = function () {
+ // Run animations
+ for (var i = 0; i < self.runAnimation.length; i++) {
+ var sprite = self.attachAsset(self.runAnimation[i], {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ sprite.alpha = i === 0 ? 1 : 0;
+ self.sprites.push(sprite);
+ }
+ // Jump animations
+ for (var i = 0; i < self.jumpAnimation.length; i++) {
+ var sprite = self.attachAsset(self.jumpAnimation[i], {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ sprite.alpha = 0;
+ self.sprites.push(sprite);
+ }
+ // Attack animations
+ for (var i = 0; i < self.attackAnimation.length; i++) {
+ var sprite = self.attachAsset(self.attackAnimation[i], {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ sprite.alpha = 0;
+ self.sprites.push(sprite);
+ }
+ };
+ // Call initialization
+ self.initAnimations();
+ // Get collision bounds
self.getBounds = function () {
return {
left: self.x - self.hitboxWidth / 2,
right: self.x + self.hitboxWidth / 2,
top: self.y - self.hitboxHeight / 2,
bottom: self.y + self.hitboxHeight / 2
};
};
- self.update = function () {
+ // Get attack hitbox
+ self.getAttackBounds = function () {
+ if (!self.isAttacking) {
+ return null;
+ }
+ return {
+ left: self.x + (self.attackHitboxOffset - self.attackHitboxWidth / 2),
+ right: self.x + (self.attackHitboxOffset + self.attackHitboxWidth / 2),
+ top: self.y - self.attackHitboxHeight / 2,
+ bottom: self.y + self.attackHitboxHeight / 2
+ };
+ };
+ // Update heart container
+ self.updateHeartContainer = function () {
self.heartContainer.x = self.x + 80;
- self.heartContainer.y = self.y - 200; // Position above player's head
- // Hide all sprites first
- for (var i = 0; i < self.sprites.length; i++) {
- self.sprites[i].alpha = 0;
+ self.heartContainer.y = self.y - 200;
+ // Handle heart visibility
+ if (self.heartVisibilityTimer > 0) {
+ self.heartVisibilityTimer--;
+ if (self.heartVisibilityTimer <= 0) {
+ self.heartContainer.alpha = 0;
+ }
}
- // Handle platform collision and falling physics first
- if (!self.isOnGround && !self.isJumping) {
- self.velocityY += 0.7;
- self.y += self.velocityY;
- self.checkPlatformCollision();
- }
+ };
+ // Update invulnerability state
+ self.updateInvulnerability = function () {
if (self.isInvulnerable) {
self.invulnerabilityTimer--;
// Flash only the player, not the hearts
self.alpha = self.invulnerabilityTimer % 10 < 5 ? 0.5 : 1;
@@ -849,48 +747,30 @@
self.isInvulnerable = false;
self.alpha = 1;
}
}
- // Separate heart visibility handling
- if (self.heartVisibilityTimer > 0) {
- self.heartVisibilityTimer--;
- if (self.heartVisibilityTimer <= 0) {
- // Hide hearts when timer runs out
- self.heartContainer.alpha = 0;
- }
- }
- // Handle jumping physics
- if (self.isJumping) {
- self.y += self.velocityY;
+ };
+ // Handle platform collision
+ self.handlePlatformCollision = function () {
+ // Already checks in jumpstate
+ if (!self.isOnGround && !self.isJumping) {
self.velocityY += 0.7;
- // Get jump frame index offset
- var jumpOffset = self.runAnimation.length;
- // Check for landing on platforms while falling during a jump
+ self.y += self.velocityY;
self.checkPlatformCollision();
- // End jump if hitting the original ground (for backward compatibility)
- if (self.y >= self.groundY && !self.currentPlatform) {
- self.y = self.groundY;
- self.isJumping = false;
- self.velocityY = 0;
- self.jumpState = "none";
- self.isOnGround = true;
- }
}
- // IMPORTANT: Platform collision check - runs EVERY frame
+ // Check if on any platform
var onAnyPlatform = false;
- var platformHalfWidth = 500; // half of platform width (1000/2)
- // First, check platform collisions
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
- var leftEdge = platform.x - platformHalfWidth;
- var rightEdge = platform.x + platformHalfWidth;
+ var leftEdge = platform.x - PLATFORM_HALF_WIDTH;
+ var rightEdge = platform.x + PLATFORM_HALF_WIDTH;
// Check if player is within horizontal bounds of platform
if (self.x >= leftEdge && self.x <= rightEdge) {
// If we're at platform height and not jumping
- if (Math.abs(self.y - (platform.y - 250)) < 5 && !self.isJumping) {
+ if (Math.abs(self.y - (platform.y - PLAYER_PLATFORM_OFFSET)) < 5 && !self.isJumping) {
onAnyPlatform = true;
self.currentPlatform = platform;
- self.y = platform.y - 250;
+ self.y = platform.y - PLAYER_PLATFORM_OFFSET;
self.isOnGround = true;
self.velocityY = 0;
break;
}
@@ -912,88 +792,105 @@
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
- // Now handle animations - attack takes priority if active
- if (self.isAttacking) {
- var attackOffset = self.runAnimation.length + self.jumpAnimation.length;
- self.animationCounter += self.attackAnimationSpeed;
- if (self.animationCounter >= 1) {
- self.animationCounter = 0;
- self.attackFrame++;
- if (self.attackFrame >= self.attackAnimation.length) {
- self.isAttacking = false;
- self.attackFrame = 0;
- }
- }
- self.sprites[attackOffset + self.attackFrame].alpha = 1;
- }
- // If not attacking, show jump or run animation
- else if (self.isJumping || !self.isOnGround) {
- var jumpOffset = self.runAnimation.length;
- var currentTime = Date.now();
- if (currentTime - self.jumpStartTime < 100) {
- self.sprites[jumpOffset + 0].alpha = 1;
- } else if (self.velocityY < 0) {
- self.sprites[jumpOffset + 1].alpha = 1;
- } else if (self.velocityY > 0) {
- self.sprites[jumpOffset + 2].alpha = 1;
- }
- } else if (self.isOnGround) {
- self.animationCounter += self.animationSpeed;
- if (self.animationCounter >= 1) {
- self.animationCounter = 0;
- self.runFrame = (self.runFrame + 1) % self.runAnimation.length;
- }
- self.sprites[self.runFrame].alpha = 1;
- }
- // If on a platform, check if we're still above it
+ // If on a platform, check if still above it
if (self.currentPlatform) {
- var platformHalfWidth = platformWidth / 2; // Use platformWidth constant
- var stillOnPlatform = self.x > self.currentPlatform.x - platformHalfWidth && self.x < self.currentPlatform.x + platformHalfWidth;
+ var stillOnPlatform = self.x > self.currentPlatform.x - PLATFORM_HALF_WIDTH && self.x < self.currentPlatform.x + PLATFORM_HALF_WIDTH;
if (!stillOnPlatform) {
var foundAnotherPlatform = false;
for (var i = 0; i < platforms.length; i++) {
var otherPlatform = platforms[i];
if (otherPlatform === self.currentPlatform) {
continue;
}
- var otherHalfWidth = platformWidth / 2;
- if (self.x > otherPlatform.x - otherHalfWidth && self.x < otherPlatform.x + otherHalfWidth) {
- // We found another platform directly below player
+ if (self.x > otherPlatform.x - PLATFORM_HALF_WIDTH && self.x < otherPlatform.x + PLATFORM_HALF_WIDTH) {
+ // Found another platform
self.currentPlatform = otherPlatform;
foundAnotherPlatform = true;
break;
}
}
- // Only fall if we didn't find another platform
+ // Fall if no other platform found
if (!foundAnotherPlatform) {
self.isOnGround = false;
self.currentPlatform = null;
- // Start falling with initial velocity
self.velocityY = 0.1;
}
}
}
- // Check if player has fallen off the bottom of the screen
- if (self.y > 2732) {
- LK.effects.flashScreen(0xff0000, 1000);
- LK.showGameOver();
+ };
+ // Update attack animation
+ self.updateAttackAnimation = function () {
+ var attackOffset = self.runAnimation.length + self.jumpAnimation.length;
+ self.animationCounter += self.attackAnimationSpeed;
+ if (self.animationCounter >= 1) {
+ self.animationCounter = 0;
+ self.attackFrame++;
+ if (self.attackFrame >= self.attackAnimation.length) {
+ self.isAttacking = false;
+ self.attackFrame = 0;
+ }
}
+ self.sprites[attackOffset + self.attackFrame].alpha = 1;
};
- self.getAttackBounds = function () {
- // Only return attack bounds when actually attacking
- if (!self.isAttacking) {
- return null;
+ // Update jump animation
+ self.updateJumpAnimation = function () {
+ var jumpOffset = self.runAnimation.length;
+ var currentTime = Date.now();
+ self.y += self.velocityY;
+ self.velocityY += 0.7;
+ // Check for landing on platforms while falling
+ self.checkPlatformCollision();
+ // End jump if hitting the ground
+ if (self.y >= self.groundY && !self.currentPlatform) {
+ self.y = self.groundY;
+ self.isJumping = false;
+ self.velocityY = 0;
+ self.jumpState = "none";
+ self.isOnGround = true;
}
- // Create a forward-facing hitbox
- return {
- left: self.x + (self.attackHitboxOffset - self.attackHitboxWidth / 2),
- right: self.x + (self.attackHitboxOffset + self.attackHitboxWidth / 2),
- top: self.y - self.attackHitboxHeight / 2,
- bottom: self.y + self.attackHitboxHeight / 2
- };
+ // Show appropriate jump frame
+ if (currentTime - self.jumpStartTime < 100) {
+ self.sprites[jumpOffset + 0].alpha = 1;
+ } else if (self.velocityY < 0) {
+ self.sprites[jumpOffset + 1].alpha = 1;
+ } else if (self.velocityY > 0) {
+ self.sprites[jumpOffset + 2].alpha = 1;
+ }
};
+ // Update run animation
+ self.updateRunAnimation = function () {
+ self.animationCounter += self.animationSpeed;
+ if (self.animationCounter >= 1) {
+ self.animationCounter = 0;
+ self.runFrame = (self.runFrame + 1) % self.runAnimation.length;
+ }
+ self.sprites[self.runFrame].alpha = 1;
+ };
+ // Check platform collision
+ self.checkPlatformCollision = function () {
+ for (var i = 0; i < platforms.length; i++) {
+ var platform = platforms[i];
+ // Check if player is above the platform and falling
+ if (self.velocityY > 0 && self.y < platform.y - PLAYER_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - PLAYER_PLATFORM_OFFSET && self.x > platform.x - PLATFORM_HALF_WIDTH && self.x < platform.x + PLATFORM_HALF_WIDTH) {
+ // Land on the platform
+ self.y = platform.y - PLAYER_PLATFORM_OFFSET;
+ self.velocityY = 0;
+ self.isJumping = false;
+ self.isOnGround = true;
+ self.currentPlatform = platform;
+ return;
+ }
+ }
+ };
+ // Hide all sprites
+ self.hideAllSprites = function () {
+ for (var i = 0; i < self.sprites.length; i++) {
+ self.sprites[i].alpha = 0;
+ }
+ };
+ // Jump method
self.jump = function () {
if (self.isOnGround) {
self.isJumping = true;
self.isOnGround = false;
@@ -1002,42 +899,23 @@
self.jumpStartTime = Date.now();
LK.getSound('playerjump').play();
self.currentPlatform = null;
} else if (self.isJumping && self.velocityY < 10) {
- // Allow for a small double-jump to reach higher platforms
- // Only if not falling too fast
+ // Small double-jump to reach higher platforms
self.velocityY = -self.jumpHeight * 0.7;
self.jumpStartTime = Date.now();
}
};
+ // Attack method
self.attack = function () {
if (!self.isAttacking) {
self.isAttacking = true;
self.attackFrame = 0;
self.animationCounter = 0;
LK.getSound('swordslash').play();
}
};
- self.checkPlatformCollision = function () {
- for (var i = 0; i < platforms.length; i++) {
- var platform = platforms[i];
- var platformHalfWidth = platform.width / 2;
- // The offset should match exactly how the player aligns with the lower platform initially
- var playerPlatformOffset = 250; // This is the exact offset between player.y and lowPlatformHeight
- // Check if player is above the platform and falling
- if (self.velocityY > 0 &&
- // Must be falling down
- self.y < platform.y - playerPlatformOffset && self.y + self.velocityY >= platform.y - playerPlatformOffset && self.x > platform.x - platformHalfWidth && self.x < platform.x + platformHalfWidth) {
- // Land on the platform with the exact same positioning as on the low platform
- self.y = platform.y - playerPlatformOffset;
- self.velocityY = 0;
- self.isJumping = false;
- self.isOnGround = true;
- self.currentPlatform = platform;
- return;
- }
- }
- };
+ // Take damage method
self.takeDamage = function () {
if (!self.isInvulnerable) {
self.currentHealth--;
self.heartContainer.updateHealth(self.currentHealth);
@@ -1060,13 +938,37 @@
LK.showGameOver();
}
}
};
+ // Main update method
+ self.update = function () {
+ // Update heart container position
+ self.updateHeartContainer();
+ // Hide all sprites
+ self.hideAllSprites();
+ // Handle platform collision and falling
+ self.handlePlatformCollision();
+ // Handle invulnerability
+ self.updateInvulnerability();
+ // Update animations based on state
+ if (self.isAttacking) {
+ self.updateAttackAnimation();
+ } else if (self.isJumping || !self.isOnGround) {
+ self.updateJumpAnimation();
+ } else if (self.isOnGround) {
+ self.updateRunAnimation();
+ }
+ // Check for falling off screen
+ if (self.y > GAME_HEIGHT) {
+ LK.effects.flashScreen(0xff0000, 1000);
+ LK.showGameOver();
+ }
+ };
+ return self;
});
-// Add this to your existing classes
var ScorePopup = Container.expand(function (x, y, amount) {
var self = Container.call(this);
- // Create the text with the actual score amount
+ // Create the text
self.text = new Text2('+' + amount, {
size: 80,
fill: 0xFFFFFF,
anchorX: 0.5,
@@ -1088,49 +990,49 @@
}
};
return self;
});
+/****
+* Constants
+****/
var Torch = Container.expand(function () {
var self = Container.call(this);
// Create base torch sprite
self.base = self.attachAsset('torch', {
anchorX: 0.5,
- anchorY: 1 // Anchor at bottom
+ anchorY: 1
});
// Create flame sprite
self.flame = self.attachAsset('torchflame', {
anchorX: 0.5,
anchorY: 1,
- // Anchor at bottom to scale from base
- y: -180 // Position at top of torch
+ y: -180
});
// Create aura sprite
self.aura = self.attachAsset('torchaura', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
- // Start with low opacity
- y: -250 // Match flame position
+ y: -250
});
// Animation properties
- self.flameTime = Math.random() * Math.PI * 2; // Random start phase
+ self.flameTime = Math.random() * Math.PI * 2;
self.auraTime = Math.random() * Math.PI * 2;
self.flameSpeed = 0.05;
self.auraSpeed = 0.03;
// Update animation
self.update = function () {
// Animate flame scale
self.flameTime += self.flameSpeed;
- var flameScale = 1 + Math.sin(self.flameTime) * 0.2; // Scale between 0.8 and 1.2
+ var flameScale = 1 + Math.sin(self.flameTime) * 0.2;
self.flame.scaleY = flameScale;
// Random flip chance for flame
if (Math.random() < 0.02) {
- // 2% chance per frame
self.flame.scaleX *= -1;
}
// Animate aura alpha
self.auraTime += self.auraSpeed;
- var auraAlpha = 0.3 + Math.sin(self.auraTime) * 0.15; // Pulse between 0.15 and 0.45
+ var auraAlpha = 0.3 + Math.sin(self.auraTime) * 0.15;
self.aura.alpha = auraAlpha;
};
return self;
});
@@ -1139,117 +1041,252 @@
// Attach chest sprite
self.sprite = self.attachAsset('treasurechest', {
anchorX: 0.5,
anchorY: 0.5,
- tint: 0xC0C0C0 // Change tint to match jar's grey tint
+ tint: 0xC0C0C0
});
- self.isBreaking = false;
- self.currentPlatform = null;
+ // Initialize as breakable
+ BreakableBehavior.initBreakable(self);
+ // Break functionality
self["break"] = function () {
- if (self.isBreaking) {
- return;
- }
- self.isBreaking = true;
- // Spawn chest pieces
- for (var i = 1; i <= 4; i++) {
- var piece = new TreasureChestPiece(i);
- piece.x = self.x;
- piece.y = self.y;
- piece.velocityX = Math.random() * 12 - 6;
- piece.velocityY = -(Math.random() * 12 + 6);
- piece.rotationSpeed = Math.random() * 0.2 - 0.1;
- game.addChild(piece);
- }
- // Spawn valuables
- var totalItems = Math.floor(Math.random() * 6) + 3; // 3-8 items
- for (var i = 0; i < totalItems; i++) {
- // Random chance for different gems
- var rand = Math.random();
- var item;
- if (rand < 0.05) {
- // 5% diamond
- item = new Coin('diamond');
- } else if (rand < 0.15) {
- // 10% emerald
- item = new Coin('emerald');
- } else if (rand < 0.30) {
- // 15% ruby
- item = new Coin('ruby');
- } else {
- // 70% gold coin
- item = new Coin('coin');
+ BreakableBehavior.standardBreak(self, TreasureChestPiece, 4, function (chest) {
+ // Spawn health potion with medium chance
+ if (Math.random() < 0.25) {
+ var potion = new HealthPotion();
+ potion.x = chest.x;
+ potion.y = chest.y;
+ potion.velocityX = GameUtils.randomRange(2, 12);
+ potion.velocityY = -GameUtils.randomRange(14, 26);
+ game.addChild(potion);
+ coins.push(potion);
}
- item.x = self.x;
- item.y = self.y;
- item.velocityX = Math.random() * 10 + 2;
- item.velocityY = -(Math.random() * 12 + 14);
- game.addChild(item);
- coins.push(item);
- }
- LK.getSound('woodbreak').play();
- self.destroy();
+ // Spawn valuable items
+ var totalItems = Math.floor(GameUtils.randomRange(3, 9));
+ for (var i = 0; i < totalItems; i++) {
+ // Random chance for different gems
+ var rand = Math.random();
+ var item;
+ if (rand < 0.05) {
+ // 5% diamond
+ item = new Coin('diamond');
+ } else if (rand < 0.15) {
+ // 10% emerald
+ item = new Coin('emerald');
+ } else if (rand < 0.30) {
+ // 15% ruby
+ item = new Coin('ruby');
+ } else {
+ // 70% gold coin
+ item = new Coin('coin');
+ }
+ item.x = chest.x;
+ item.y = chest.y;
+ item.velocityX = GameUtils.randomRange(2, 12);
+ item.velocityY = -GameUtils.randomRange(14, 26);
+ game.addChild(item);
+ coins.push(item);
+ }
+ LK.getSound('woodbreak').play();
+ });
};
return self;
});
var TreasureChestPiece = Container.expand(function (pieceNum) {
var self = Container.call(this);
+ // Attach piece sprite
self.sprite = self.attachAsset('treasurechestpiece' + pieceNum, {
anchorX: 0.5,
anchorY: 0.5,
- tint: 0xC0C0C0 // Change tint to match jar's grey tint
+ tint: 0xC0C0C0
});
- self.velocityX = 0;
- self.velocityY = 0;
- self.rotationSpeed = 0;
- self.fadeSpeed = 0.02;
- self.bounceCount = 0;
- self.maxBounces = 2;
+ // Initialize as piece
+ PieceBehavior.initPiece(self);
+ // Update method
self.update = function () {
- self.velocityY += 0.5; // gravity
- self.x += self.velocityX;
- self.y += self.velocityY;
- self.rotation += self.rotationSpeed;
- if (self.checkPlatformCollision()) {
- if (self.bounceCount < self.maxBounces) {
- self.velocityY = -(self.velocityY * 0.4);
- self.velocityX *= 0.8;
- self.bounceCount++;
- } else {
- self.velocityY = 0;
- self.velocityX = -5;
- self.alpha -= self.fadeSpeed;
- if (self.alpha <= 0) {
- self.destroy();
- }
- }
- }
- if (self.x < -50 || self.y > 2732) {
- self.destroy();
- }
+ PieceBehavior.standardUpdate(self);
};
self.checkPlatformCollision = function () {
- for (var i = 0; i < platforms.length; i++) {
- var platform = platforms[i];
- if (Math.abs(self.y - (platform.y - 80)) < 10 && self.x > platform.x - 500 && self.x < platform.x + 500) {
- self.y = platform.y - 80;
- return true;
- }
- }
- return false;
+ return GameUtils.checkPlatformCollision(self, 80, true) != null;
};
return self;
});
/****
* Initialize Game
****/
+/****
+* Game Initialization
+****/
var game = new LK.Game({
- backgroundColor: 0x000000 // Black background
+ backgroundColor: 0x000000
});
/****
* Game Code
****/
+// Base collectible behavior for items that can be collected
+/****
+* Game Management
+****/
+var CollectibleBehavior = {
+ initPhysics: function initPhysics(self) {
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.collected = false;
+ self.bounceCount = 0;
+ self.maxBounces = 2;
+ },
+ standardUpdate: function standardUpdate(self) {
+ if (self.collected) {
+ return;
+ }
+ // Apply physics
+ self.velocityY += 0.5; // gravity
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ // Check for platform collision with bounce
+ if (self.velocityY > 0) {
+ GameUtils.checkPlatformCollision(self, 80, true);
+ }
+ // Check if off screen
+ if (self.x < -50 || self.x > GAME_WIDTH + 50 || self.y > GAME_HEIGHT) {
+ self.destroy();
+ return;
+ }
+ // Player collection detection
+ var playerBounds = player.getBounds();
+ var itemBounds = {
+ left: self.x - 25,
+ right: self.x + 25,
+ top: self.y - 25,
+ bottom: self.y + 25
+ };
+ if (GameUtils.checkCollision(playerBounds, itemBounds)) {
+ self.collect();
+ }
+ }
+};
+// Base behavior for breakable objects
+var BreakableBehavior = {
+ initBreakable: function initBreakable(self) {
+ self.isBreaking = false;
+ self.currentPlatform = null;
+ },
+ standardBreak: function standardBreak(self, pieceClass, pieceCount, itemSpawnCallback) {
+ if (self.isBreaking) {
+ return;
+ }
+ self.isBreaking = true;
+ // Spawn pieces
+ for (var i = 1; i <= pieceCount; i++) {
+ var piece = new pieceClass(i);
+ piece.x = self.x;
+ piece.y = self.y;
+ piece.velocityX = GameUtils.randomRange(-6, 6);
+ piece.velocityY = -GameUtils.randomRange(6, 12);
+ piece.rotationSpeed = GameUtils.randomRange(-0.1, 0.1);
+ game.addChild(piece);
+ }
+ // Call the custom item spawn callback
+ if (itemSpawnCallback) {
+ itemSpawnCallback(self);
+ }
+ self.destroy();
+ }
+};
+// Base behavior for pieces of broken objects
+var PieceBehavior = {
+ initPiece: function initPiece(self) {
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.rotationSpeed = 0;
+ self.fadeSpeed = 0.02;
+ self.bounceCount = 0;
+ self.maxBounces = 2;
+ },
+ standardUpdate: function standardUpdate(self) {
+ // Apply physics
+ self.velocityY += 0.5; // gravity
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ self.rotation += self.rotationSpeed;
+ // Check for platform collision
+ var platformCollision = GameUtils.checkPlatformCollision(self, 80, self.bounceCount < self.maxBounces);
+ if (platformCollision && self.bounceCount >= self.maxBounces) {
+ // Start fading after max bounces
+ self.velocityY = 0;
+ self.velocityX = -PLATFORM_SPEED;
+ self.alpha -= self.fadeSpeed;
+ if (self.alpha <= 0) {
+ self.destroy();
+ }
+ }
+ // Destroy if off screen
+ if (self.x < -50 || self.y > GAME_HEIGHT) {
+ self.destroy();
+ }
+ }
+};
+/****
+* Constants
+****/
+var GAME_WIDTH = 2048;
+var GAME_HEIGHT = 2732;
+var PLATFORM_WIDTH = 1000;
+var PLATFORM_HALF_WIDTH = PLATFORM_WIDTH / 2;
+var PLATFORM_OVERLAP = 50;
+var PLATFORM_SPEED = 5;
+var ENEMY_PLATFORM_OFFSET = 225;
+var PLAYER_PLATFORM_OFFSET = 250;
+var MIN_PLATFORMS_IN_SEQUENCE = 2;
+var MAX_PLATFORMS_IN_SEQUENCE = 5;
+var JUMP_COOLDOWN = 250;
+var MOVE_THRESHOLD = 40;
+/****
+* Utilities
+****/
+var GameUtils = {
+ // Check for collision between two rectangular bounds
+ checkCollision: function checkCollision(bounds1, bounds2) {
+ return bounds1.left < bounds2.right && bounds1.right > bounds2.left && bounds1.top < bounds2.bottom && bounds1.bottom > bounds2.top;
+ },
+ // Check platform collision with standard offset
+ checkPlatformCollision: function checkPlatformCollision(obj, offsetY, bounceOnCollision) {
+ for (var i = 0; i < platforms.length; i++) {
+ var platform = platforms[i];
+ if (Math.abs(obj.y - (platform.y - offsetY)) < 10 && obj.x > platform.x - PLATFORM_HALF_WIDTH && obj.x < platform.x + PLATFORM_HALF_WIDTH) {
+ obj.y = platform.y - offsetY;
+ if (bounceOnCollision && obj.bounceCount < obj.maxBounces) {
+ LK.getSound('coinbounce').play();
+ var impactSpeed = Math.abs(obj.velocityY);
+ obj.velocityY = -(impactSpeed * 0.5);
+ obj.velocityX *= 0.8;
+ obj.bounceCount++;
+ return true;
+ } else if (bounceOnCollision) {
+ obj.velocityY = 0;
+ obj.velocityX = -PLATFORM_SPEED;
+ }
+ return platform;
+ }
+ }
+ return null;
+ },
+ // Get random value within range
+ randomRange: function randomRange(min, max) {
+ return Math.random() * (max - min) + min;
+ },
+ // Check if position is clear for spawning
+ canSpawnAtPosition: function canSpawnAtPosition(x) {
+ var safeDistance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 200;
+ for (var i = 0; i < collectibles.length; i++) {
+ if (Math.abs(collectibles[i].x - x) < safeDistance) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
var ScoreManager = function ScoreManager() {
var self = {};
// Initialize score and UI
self.score = 0;
@@ -1269,9 +1306,9 @@
self.container = new Container();
self.container.addChild(self.scoreText);
self.container.addChild(self.coinIcon);
// Position container
- self.container.x = 2048 - 100;
+ self.container.x = GAME_WIDTH - 100;
self.container.y = 100;
// Position elements
self.scoreText.x = -100;
self.scoreText.y = -100;
@@ -1285,150 +1322,143 @@
self.score = newScore;
self.scoreText.setText(newScore);
// Adjust position based on digit count
if (newScore >= 10 && newScore < 100) {
- self.scoreText.x = -200; // Adjust for 2 digits
+ self.scoreText.x = -200;
} else if (newScore >= 100 && newScore < 1000) {
- self.scoreText.x = -300; // Adjust for 3 digits
+ self.scoreText.x = -300;
} else if (newScore >= 1000) {
- self.scoreText.x = -400; // Adjust for 4+ digits
+ self.scoreText.x = -400;
} else {
- self.scoreText.x = -100; // Default for single digit
+ self.scoreText.x = -100;
}
};
// Add score and display popup
self.addScore = function (amount, x, y) {
self.setScore(self.score + amount);
- LK.setScore(self.score); // Update LK score
+ LK.setScore(self.score);
// Create score popup if position is provided
if (x !== undefined && y !== undefined) {
var popup = new ScorePopup(x, y - 30, amount);
game.addChild(popup);
}
};
return self;
};
+/****
+* Game Variables
+****/
+// Containers
var backgroundContainer = game.addChild(new Container());
var midgroundContainer = game.addChild(new Container());
var foregroundContainer = game.addChild(new Container());
var scoreManager = new ScoreManager();
game.addChild(scoreManager.container);
var scoreContainer = game.addChild(new Container());
-var GAME_WIDTH = 2048;
-var ENEMY_PLATFORM_OFFSET = 225; // Adjust this value
+// Game state
var gameStarted = false;
var titleScreen;
-var playButton; // Define playButton in the global scope
+var playButton;
+// Platform management
var platforms = [];
-var platformWidth = 1000; // Define platform width based on asset size
-var platformSpawnInterval = 60;
var platformSpawnCounter = 0;
-var platformOverlap = 50;
-var platformSpeed = 5; // Define platform speed globally
-var platformSpawnInterval = 190;
-var chestSpawnInterval = 100; // Longer than jar interval
-var chestSpawnCounter = 0;
-// = (1000 - 50) / 5 = 190
-// Define the two fixed platform heights
-var lowPlatformHeight = 2732 / 1.5 + 250; // Regular height (player.y + 250)
-var highPlatformHeight = lowPlatformHeight - 600; // High platform (600 pixels higher)
-var minPlatformsInSequence = 2; // Minimum platforms in a sequence
-var maxPlatformsInSequence = 5; // Maximum platforms in a sequence
-var platformsUntilNextChange = 5; // Initial longer ground sequence
+var platformsUntilNextChange = 5;
+var lowPlatformHeight = GAME_HEIGHT / 1.5 + PLAYER_PLATFORM_OFFSET;
+var highPlatformHeight = lowPlatformHeight - 600;
var currentPlatformHeight = lowPlatformHeight;
var lastPlatformHeight = lowPlatformHeight;
-// Simple toggle for alternating platform heights
-var useHighPlatform = false;
-var platformGap = 200; // Gap between platforms
-var lastPlatformX = 0; // Track the last platform position
+var lastPlatformX = 0;
+// Touch controls
var touchStartX = 0;
var touchStartY = 0;
var touchEndX = 0;
var touchEndY = 0;
var lastMoveY = 0;
-var moveThreshold = 30; // Adjust this value to control sensitivity
var lastJumpTime = 0;
-var jumpCooldown = 250;
+// Game objects
var jars = [];
var coins = [];
var collectibles = [];
var jarSpawnCounter = 0;
-var jarSpawnInterval = 20; // Adjust as needed
-var player;
-// Initialize enemies
+var jarSpawnInterval = 20;
+var chestSpawnCounter = 0;
+var chestSpawnInterval = 100;
var enemies = [];
var enemySpawnInterval = 100;
-var enemySpawnCounter = 0;
-var goblinSpawnInterval = 100;
var goblinSpawnCounter = 0;
-var eyeballSpawnInterval = 500; // Longer interval for eyeballs
+var goblinSpawnInterval = 100;
var eyeballSpawnCounter = 150;
+var eyeballSpawnInterval = 500;
var particleSystem;
var heartContainer = new HeartContainer();
-// Background layer (slowest)
+var player;
+// Background elements
var bg1 = backgroundContainer.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 1
}));
var bg2 = backgroundContainer.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 1
}));
-bg1.y = 2732; // Position at bottom of screen
-bg2.y = 2732;
+bg1.y = GAME_HEIGHT;
+bg2.y = GAME_HEIGHT;
bg2.x = GAME_WIDTH;
-// Midground layer
+// Midground elements
var mg1 = midgroundContainer.addChild(LK.getAsset('midground', {
anchorX: 0,
anchorY: 1
}));
var mg2 = midgroundContainer.addChild(LK.getAsset('midground', {
anchorX: 0,
anchorY: 1
}));
-mg1.y = 2732;
-mg2.y = 2732;
+mg1.y = GAME_HEIGHT;
+mg2.y = GAME_HEIGHT;
mg2.x = GAME_WIDTH;
-// Foreground layer (fastest)
+// Foreground elements
var fg1 = foregroundContainer.addChild(LK.getAsset('foreground', {
anchorX: 0,
anchorY: 1
}));
var fg2 = foregroundContainer.addChild(LK.getAsset('foreground', {
anchorX: 0,
anchorY: 1
}));
-fg1.y = 2732 * 1.25;
-fg2.y = 2732 * 1.25;
+fg1.y = GAME_HEIGHT * 1.25;
+fg2.y = GAME_HEIGHT * 1.25;
fg1.x = 0;
fg2.x = GAME_WIDTH;
+/****
+* Game Functions
+****/
+// Create the title screen
function createTitleScreen() {
titleScreen = new Container();
game.addChild(titleScreen);
- // Add title image
+ // Add title image with fade-in
var titleImage = titleScreen.attachAsset('title', {
anchorX: 0.5,
anchorY: 0.5,
- alpha: 0 // Start with alpha 0 for fade-in effect
+ alpha: 0
});
- titleImage.x = 2048 / 2;
- titleImage.y = 2732 / 2.7;
- // Add fade-in animation for titleImage
+ titleImage.x = GAME_WIDTH / 2;
+ titleImage.y = GAME_HEIGHT / 2.7;
+ // Fade in title
tween(titleImage, {
alpha: 1
}, {
duration: 1000,
- // 1 second fade-in duration
easing: tween.easeIn
});
// Add play button
playButton = titleScreen.attachAsset('playbutton', {
anchorX: 0.5,
anchorY: 0.5
});
- playButton.x = 2048 / 2;
- playButton.y = 2732 / 1.3;
- // Add flashing animation to playButton
+ playButton.x = GAME_WIDTH / 2;
+ playButton.y = GAME_HEIGHT / 1.3;
+ // Add flashing animation
function flashPlayButton() {
tween(playButton, {
alpha: 0
}, {
@@ -1443,41 +1473,31 @@
});
}
});
}
- // Set interval to flash every 2 seconds
+ // Flash every 2 seconds
LK.setInterval(flashPlayButton, 2000);
+ // Initialize torch decorations
initializeTorches();
}
-function canSpawnAtPosition(x, safeDistance) {
- safeDistance = safeDistance || 200; // Minimum distance between objects
- for (var i = 0; i < collectibles.length; i++) {
- var existingItem = collectibles[i];
- if (Math.abs(existingItem.x - x) < safeDistance) {
- return false;
- }
- }
- return true;
-}
+// Place torches in the scene
function initializeTorches() {
- // Calculate number of torches needed based on screen width
- var torchSpacing = GAME_WIDTH; // One torch per background width
- // Create two torches for the two background sections
+ // Create torches for the two background sections
var torch1 = new Torch();
torch1.x = 25;
- torch1.y = 2732 * 0.7; // Adjust Y position as needed
+ torch1.y = GAME_HEIGHT * 0.7;
midgroundContainer.addChild(torch1);
var torch2 = new Torch();
torch2.x = GAME_WIDTH + 25;
- torch2.y = 2732 * 0.7;
+ torch2.y = GAME_HEIGHT * 0.7;
midgroundContainer.addChild(torch2);
}
-// Initialize the game with starting platforms
+// Initialize game elements
function initializeGame() {
- // Initialize player
+ // Create player
player = game.addChild(new Player());
- player.x = 2048 / 4.5;
- player.y = 2732 / 1.5;
+ player.x = GAME_WIDTH / 4.5;
+ player.y = GAME_HEIGHT / 1.5;
game.addChild(heartContainer);
// Create initial platforms at the low level
for (var i = 0; i < 5; i++) {
var platform = new Platform();
@@ -1485,9 +1505,9 @@
// First platform centered on player
platform.x = player.x;
} else {
// Position with slight overlap
- platform.x = lastPlatformX + platformWidth - platformOverlap;
+ platform.x = lastPlatformX + PLATFORM_WIDTH - PLATFORM_OVERLAP;
}
platform.y = lowPlatformHeight;
platforms.push(platform);
game.addChild(platform);
@@ -1496,17 +1516,17 @@
lastPlatformHeight = lowPlatformHeight;
player.isOnGround = true;
player.currentPlatform = platforms[0];
}
+// Start the game
function startGame() {
gameStarted = true;
- // Remove title screen
titleScreen.destroy();
- // Initialize game elements
initializeGame();
// Initialize particle system
particleSystem = new ParticlePool(100);
game.addChild(particleSystem);
+ // Show health
player.heartContainer.alpha = 1;
player.heartVisibilityTimer = 120;
// Play background music
LK.playMusic('backgroundmusic1', {
@@ -1516,74 +1536,68 @@
duration: 1000
}
});
}
-createTitleScreen();
-// Handle game updates
-game.update = function () {
- // Always scroll backgrounds for visual effect even on title screen
- bg1.x -= platformSpeed * 0.3;
- bg2.x -= platformSpeed * 0.3;
+// Update background layers
+function updateBackgrounds() {
+ // Background layer (slowest)
+ bg1.x -= PLATFORM_SPEED * 0.3;
+ bg2.x -= PLATFORM_SPEED * 0.3;
if (bg1.x <= -GAME_WIDTH) {
bg1.x = bg2.x + GAME_WIDTH;
}
if (bg2.x <= -GAME_WIDTH) {
bg2.x = bg1.x + GAME_WIDTH;
}
- mg1.x -= platformSpeed * 0.6;
- mg2.x -= platformSpeed * 0.6;
+ // Midground layer
+ mg1.x -= PLATFORM_SPEED * 0.6;
+ mg2.x -= PLATFORM_SPEED * 0.6;
if (mg1.x <= -GAME_WIDTH) {
mg1.x = mg2.x + GAME_WIDTH;
}
if (mg2.x <= -GAME_WIDTH) {
mg2.x = mg1.x + GAME_WIDTH;
}
- // Also scroll foreground on title screen
- fg1.x -= platformSpeed;
- fg2.x -= platformSpeed;
+ // Foreground layer (fastest)
+ fg1.x -= PLATFORM_SPEED;
+ fg2.x -= PLATFORM_SPEED;
if (fg1.x <= -GAME_WIDTH) {
fg1.x = fg2.x + GAME_WIDTH;
}
if (fg2.x <= -GAME_WIDTH) {
fg2.x = fg1.x + GAME_WIDTH;
}
+ // Update torches
for (var i = 0; i < midgroundContainer.children.length; i++) {
var child = midgroundContainer.children[i];
if (child instanceof Torch) {
child.update();
- // Move torch with midground
- child.x -= platformSpeed * 0.6;
- // Reset torch position when it goes off screen
+ child.x -= PLATFORM_SPEED * 0.6;
if (child.x <= -GAME_WIDTH) {
child.x = child.x + GAME_WIDTH * 2;
}
}
}
- if (!gameStarted) {
- return;
- }
- player.update();
- if (particleSystem) {
- particleSystem.update();
- }
+}
+// Update and spawn platforms
+function updatePlatforms() {
// Check if we need to spawn a new sequence of platforms
var lastPlatform = platforms[platforms.length - 1];
if (lastPlatform && lastPlatform.x < GAME_WIDTH + 500) {
- // Spawn further off-screen to prevent pop-in
// Time to spawn a new sequence
if (platformsUntilNextChange <= 0) {
// Switch height
currentPlatformHeight = currentPlatformHeight === lowPlatformHeight ? highPlatformHeight : lowPlatformHeight;
// Generate new random sequence length
- platformsUntilNextChange = Math.floor(Math.random() * (maxPlatformsInSequence - minPlatformsInSequence + 1)) + minPlatformsInSequence;
+ platformsUntilNextChange = Math.floor(Math.random() * (MAX_PLATFORMS_IN_SEQUENCE - MIN_PLATFORMS_IN_SEQUENCE + 1)) + MIN_PLATFORMS_IN_SEQUENCE;
// Make ground sequences longer
if (currentPlatformHeight === lowPlatformHeight) {
platformsUntilNextChange += 5;
}
}
// Spawn a single platform in the sequence
var platform = new Platform();
- platform.x = lastPlatform.x + (platformWidth - platformOverlap);
+ platform.x = lastPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
platform.y = currentPlatformHeight;
platforms.push(platform);
game.addChild(platform);
// Decrement the counter for each platform added
@@ -1596,48 +1610,52 @@
if (platforms[i].destroyed) {
platforms.splice(i, 1);
}
}
+}
+// Update and spawn collectibles
+function updateCollectibles() {
+ // Jar spawning
jarSpawnCounter++;
if (jarSpawnCounter >= jarSpawnInterval) {
var availablePlatforms = platforms.filter(function (p) {
- return p.x > 2048 && p.x < 2048 + 300;
+ return p.x > GAME_WIDTH && p.x < GAME_WIDTH + 300;
});
if (availablePlatforms.length > 0 && Math.random() < 0.3) {
var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)];
// Only spawn if position is clear
- if (canSpawnAtPosition(platform.x)) {
+ if (GameUtils.canSpawnAtPosition(platform.x)) {
var jar = new Jar();
jar.x = platform.x;
jar.y = platform.y - 130;
jar.currentPlatform = platform;
- collectibles.push(jar); // Add to collectibles array instead of jars array
+ collectibles.push(jar);
game.addChild(jar);
}
}
jarSpawnCounter = 0;
}
+ // Treasure chest spawning
chestSpawnCounter++;
if (chestSpawnCounter >= chestSpawnInterval) {
var availablePlatforms = platforms.filter(function (p) {
- return p.x > 2048 && p.x < 2048 + 300;
+ return p.x > GAME_WIDTH && p.x < GAME_WIDTH + 300;
});
if (availablePlatforms.length > 0 && Math.random() < 0.15) {
- // 15% chance to spawn
var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)];
// Only spawn if position is clear
- if (canSpawnAtPosition(platform.x)) {
+ if (GameUtils.canSpawnAtPosition(platform.x)) {
var chest = new TreasureChest();
chest.x = platform.x;
chest.y = platform.y - 130;
chest.currentPlatform = platform;
- collectibles.push(chest); // Add to collectibles array
+ collectibles.push(chest);
game.addChild(chest);
}
}
chestSpawnCounter = 0;
}
- // Replace the existing jar update code with this:
+ // Update existing collectibles
for (var i = collectibles.length - 1; i >= 0; i--) {
var collectible = collectibles[i];
if (collectible.currentPlatform) {
collectible.x = collectible.currentPlatform.x;
@@ -1649,9 +1667,9 @@
right: collectible.x + 50,
top: collectible.y - 75,
bottom: collectible.y + 75
};
- if (attackBounds.left < itemBounds.right && attackBounds.right > itemBounds.left && attackBounds.top < itemBounds.bottom && attackBounds.bottom > itemBounds.top) {
+ if (GameUtils.checkCollision(attackBounds, itemBounds)) {
collectible["break"]();
collectibles.splice(i, 1);
continue;
}
@@ -1660,26 +1678,30 @@
collectible.destroy();
collectibles.splice(i, 1);
}
}
- // Update coins
+ // Update coins and other collectibles
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].update();
if (coins[i].destroyed) {
coins.splice(i, 1);
}
}
+ // Update score popups
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof ScorePopup) {
child.update();
}
}
- // Handle goblin spawning
+}
+// Update and spawn enemies
+function updateEnemies() {
+ // Goblin spawning
goblinSpawnCounter++;
if (goblinSpawnCounter >= goblinSpawnInterval) {
var availablePlatforms = platforms.filter(function (p) {
- return p.x > 2048 - 100 && p.x < 2048 + 300;
+ return p.x > GAME_WIDTH - 100 && p.x < GAME_WIDTH + 300;
});
if (availablePlatforms.length > 0) {
var enemy = new Enemy('goblin');
var platform = availablePlatforms[Math.floor(Math.random() * availablePlatforms.length)];
@@ -1690,38 +1712,37 @@
game.addChild(enemy);
goblinSpawnInterval = Math.floor(Math.random() * 150) + 100;
goblinSpawnCounter = 0;
} else {
- // No valid platforms, just reset counter but don't spawn
- goblinSpawnCounter = Math.max(0, goblinSpawnCounter - 20); // Back up a bit to try again soon
+ // No valid platforms, reset counter but don't spawn
+ goblinSpawnCounter = Math.max(0, goblinSpawnCounter - 20);
}
}
- // Handle eyeball spawning
+ // Eyeball spawning
eyeballSpawnCounter++;
if (eyeballSpawnCounter >= eyeballSpawnInterval) {
var enemy = new Enemy('eyeball');
var randomHeight = Math.random() * 400 + 200;
- enemy.x = 2048 + 100;
+ enemy.x = GAME_WIDTH + 100;
enemy.y = highPlatformHeight - randomHeight;
enemies.push(enemy);
game.addChild(enemy);
eyeballSpawnInterval = Math.floor(Math.random() * 300) + 150;
eyeballSpawnCounter = 0;
}
- // Update enemies
+ // Update enemies and check collisions
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
+ // Skip if enemy is far behind player
+ if (enemies[j].x < player.x - 100) {
+ continue;
+ }
var playerBounds = player.getBounds();
var enemyBounds = enemies[j].getBounds();
var attackBounds = player.getAttackBounds();
- // Add check if enemy is behind player
- if (enemies[j].x < player.x - 100) {
- // Added 100px buffer
- continue; // Skip collision checks for enemies behind player
- }
// Check for attack collision first
if (attackBounds && !enemies[j].isHit && !enemies[j].isDying) {
- if (enemies[j].x > player.x && attackBounds.left < enemyBounds.right && attackBounds.right > enemyBounds.left && attackBounds.top < enemyBounds.bottom && attackBounds.bottom > enemyBounds.top) {
+ if (enemies[j].x > player.x && GameUtils.checkCollision(attackBounds, enemyBounds)) {
enemies[j].hit();
if (enemies[j].type === 'eyeball') {
LK.getSound('eyeballhit').play();
} else {
@@ -1730,16 +1751,39 @@
continue;
}
}
// Check for body collision second
- if (playerBounds.left < enemyBounds.right && playerBounds.right > enemyBounds.left && playerBounds.top < enemyBounds.bottom && playerBounds.bottom > enemyBounds.top) {
+ if (GameUtils.checkCollision(playerBounds, enemyBounds)) {
if (!enemies[j].isHit && !enemies[j].isDying) {
player.takeDamage();
}
}
}
+}
+// Create the initial title screen
+createTitleScreen();
+/****
+* Game Loop & Input Handlers
+****/
+// Main game update loop
+game.update = function () {
+ // Always update backgrounds for visual effect
+ updateBackgrounds();
+ // If game hasn't started, don't update gameplay
+ if (!gameStarted) {
+ return;
+ }
+ // Update game entities
+ player.update();
+ if (particleSystem) {
+ particleSystem.update();
+ }
+ // Update game world
+ updatePlatforms();
+ updateCollectibles();
+ updateEnemies();
};
-// Handle player jump
+// Handle touch/click events
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
};
@@ -1761,9 +1805,9 @@
var deltaY = touchStartY - touchEndY;
var deltaX = Math.abs(touchStartX - touchEndX);
var currentTime = Date.now();
if (deltaY > 50 && deltaY > deltaX) {
- if (currentTime - lastJumpTime > jumpCooldown) {
+ if (currentTime - lastJumpTime > JUMP_COOLDOWN) {
player.jump();
lastJumpTime = currentTime;
}
} else {
@@ -1777,9 +1821,9 @@
}
if (lastMoveY !== 0) {
var deltaY = y - lastMoveY;
var currentTime = Date.now();
- if (deltaY < -moveThreshold && currentTime - lastJumpTime > jumpCooldown) {
+ if (deltaY < -MOVE_THRESHOLD && currentTime - lastJumpTime > JUMP_COOLDOWN) {
player.jump();
lastJumpTime = currentTime;
}
}
2D Single Monster. In-Game asset. 2d. Blank background. High contrast. No shadows..
A gold coin. 8 bit pixel art. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Dark and moody dungeon background. Infinite repeatable texture. 8 bit pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A ruby. Pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A wooden arrow with white feathers and a steel arrow head. Horizontal. Pixel art. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
backgroundmusic1
Music
playerjump
Sound effect
swordslash
Sound effect
jarbreak
Sound effect
enemyhit
Sound effect
eyeballhit
Sound effect
coincollect
Sound effect
woodbreak
Sound effect
coinbounce
Sound effect
potion
Sound effect
playerouch
Sound effect
bowfiring
Sound effect
arrowfire
Sound effect
arrowpickup
Sound effect
gameover
Sound effect