Code edit (2 edits merged)
Please save this source code
User prompt
Update with: if (!self.initialThrowMade && self.x < GAME_WIDTH + 300 && self.isOnGround && !self.isThrowing) { if (LK.ticks - self.enteredScreenTime > self.throwDelay) { self.isThrowing = true; self.throwingTimer = self.throwingDuration; self.throwingFrame = 0; self.originalSpeed = self.speed; // Remove the speed = 5 line since we're using platform speed during throw } }
User prompt
Update with: self.updateSkeletonThrowingState = function () { // Always move exactly with platform speed during throwing self.x -= PLATFORM_SPEED * gameSpeedMultiplier; // Offset to find throwing animation sprites var throwOffset = self.runAnimation.length + self.hitAnimation.length + self.dieAnimation.length; // Progress through throwing frames if (self.throwingTimer > self.throwingDuration - self.throwingPauseTime) { // Early frames building up to throw var frameIndex = Math.floor((self.throwingDuration - self.throwingTimer) / ((self.throwingDuration - self.throwingPauseTime) / 5)); frameIndex = Math.min(4, Math.max(0, frameIndex)); self.sprites[throwOffset + frameIndex].alpha = 1; } else if (self.throwingTimer === Math.floor(self.throwingDuration * 0.8)) { var sword = new SkeletonSword(); sword.x = self.x - 100; sword.y = self.y - 100; game.addChild(sword); skeletonSwords.push(sword); self.sprites[throwOffset + 5].alpha = 1; } else if (self.throwingTimer <= self.throwingPauseTime) { // During pause, keep showing the final frame self.sprites[throwOffset + 5].alpha = 1; } else { // Middle of animation (after throw, before pause) self.sprites[throwOffset + 5].alpha = 1; } // Decrease timer self.throwingTimer--; // End throwing state and restore speed if (self.throwingTimer <= 0) { self.isThrowing = false; self.speed = self.originalSpeed || 6; } };
Code edit (1 edits merged)
Please save this source code
User prompt
Replace this section with: case self.states.DOUBLE_JUMP: if (self.firstJumpPerformed && self.secondJumpPerformed && player.isOnGround && player.currentPlatform && player.currentPlatform.y === HIGHEST_PLATFORM_HEIGHT && !self.removingPlatforms) { // Move to transition state instead of immediately removing platforms self.currentState = self.states.TRANSITIONING_TO_SLIDE; self.platformRemovalTimer = 60; self.removingPlatforms = true; } break;
User prompt
Update with: self.states = { ATTACK: 'attack', BOW: 'bow', JUMP: 'jump', DROP_DOWN: 'drop_down', DOUBLE_JUMP: 'double_jump', TRANSITIONING_TO_SLIDE: 'transitioning_to_slide', // New state SLIDE: 'slide', COMPLETE: 'complete' };
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: case self.states.DOUBLE_JUMP: if (self.firstJumpPerformed && self.secondJumpPerformed && player.isOnGround && player.currentPlatform && player.currentPlatform.y === HIGHEST_PLATFORM_HEIGHT && !self.removingPlatforms) { // Add check to prevent multiple triggers // Start platform removal sequence self.platformRemovalTimer = 60; self.removingPlatforms = true; } // Handle platform removal timer if (self.removingPlatforms) { if (self.platformRemovalTimer > 0) { self.platformRemovalTimer--; if (self.platformRemovalTimer === 0) { // Remove all high platforms after delay for (var i = platforms.length - 1; i >= 0; i--) { if (platforms[i].y < lowPlatformHeight) { platforms[i].destroy(); platforms.splice(i, 1); } } } } // Check for transition to slide state after platforms are removed if (self.platformRemovalTimer === 0 && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) { self.currentState = self.states.SLIDE; self.setMessage("Swipe right to slide"); self.removingPlatforms = false; // Reset jump tracking self.firstJumpPerformed = false; self.secondJumpPerformed = false; } } break;
Code edit (1 edits merged)
Please save this source code
User prompt
Update as needed with: case self.states.SLIDE: // Handle platform removal timer if (self.platformRemovalTimer > 0) { self.platformRemovalTimer--; if (self.platformRemovalTimer === 0) { // Remove all high platforms after delay for (var i = platforms.length - 1; i >= 0; i--) { if (platforms[i].y < lowPlatformHeight) { platforms[i].destroy(); platforms.splice(i, 1); } } } } // Only spawn sword if in slide state and player is on ground if (!self.slideSword && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) { self.slideSword = new SkeletonSword(); self.slideSword.x = GAME_WIDTH + 100; self.slideSword.y = player.y - 120; game.addChild(self.slideSword); } // ... rest of slide state code
User prompt
Replace this section: case self.states.DOUBLE_JUMP: if (self.firstJumpPerformed && self.secondJumpPerformed && player.isOnGround && player.currentPlatform && player.currentPlatform.y === HIGHEST_PLATFORM_HEIGHT) { // Start platform removal sequence but don't change state yet self.platformRemovalTimer = 60; // 1 second at 60fps self.removingPlatforms = true; // Add this flag // Reset jump tracking self.firstJumpPerformed = false; self.secondJumpPerformed = false; } // Check if platforms are gone and player has landed on lower platform if (self.removingPlatforms && self.platformRemovalTimer === 0 && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) { self.currentState = self.states.SLIDE; self.setMessage("Swipe right to slide"); self.removingPlatforms = false; } break;
User prompt
Update with: self.init = function () { // ... existing init code ... self.platformRemovalTimer = 0; self.removingPlatforms = false; self.hasSlideSucceeded = false; // ... rest of init code }
User prompt
Update as needed with: self.init = function () { // ... existing init code ... self.platformRemovalTimer = 0; self.readyForSword = false; self.hasSlideSucceeded = false; // ... rest of init code }
User prompt
Update as needed with: case self.states.DOUBLE_JUMP: if (self.firstJumpPerformed && self.secondJumpPerformed && player.isOnGround && player.currentPlatform && player.currentPlatform.y === HIGHEST_PLATFORM_HEIGHT) { self.currentState = self.states.SLIDE; self.setMessage("Swipe right to slide"); self.platformRemovalTimer = 60; // 1 second at 60fps self.readyForSword = false; // Add this flag // Reset jump tracking self.firstJumpPerformed = false; self.secondJumpPerformed = false; } break; case self.states.SLIDE: // Handle platform removal timer if (self.platformRemovalTimer > 0) { self.platformRemovalTimer--; if (self.platformRemovalTimer === 0) { // Remove all high platforms after delay for (var i = platforms.length - 1; i >= 0; i--) { if (platforms[i].y < lowPlatformHeight) { platforms[i].destroy(); platforms.splice(i, 1); } } } } // Only set ready for sword when player lands on lower platform if (!self.readyForSword && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) { self.readyForSword = true; } // Only spawn sword if ready and player is on ground if (!self.slideSword && self.readyForSword && player.isOnGround && !player.isJumping) { self.slideSword = new SkeletonSword(); self.slideSword.x = GAME_WIDTH + 100; self.slideSword.y = player.y - 120; game.addChild(self.slideSword); }
User prompt
Flash the screen red with the skeleton sword hits the player in the tutorial. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Wait a second before removing all the platforms at the end of the double jump phase in the tutorial.
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: case self.states.SLIDE: // Track successful slide with a new flag if (!self.hasSlideSucceeded && self.slideSword && self.slideSword.x < player.x && player.isSliding) { self.hasSlideSucceeded = true; } // Complete tutorial when player has finished sliding and succeeded if (self.hasSlideSucceeded && !player.isSliding) { self.currentState = self.states.COMPLETE; self.setMessage("Tutorial Complete!"); LK.setTimeout(function () { self.complete(); }, 2000); } break;
Code edit (2 edits merged)
Please save this source code
User prompt
Update with: case self.states.SLIDE: // Only check for completion if: // 1. The sword exists and has passed the player // 2. The player successfully slid under it (didn't get hit) // 3. The sword has left the screen // 4. The player has finished their slide animation if (self.slideSword && self.slideSword.x < player.x && self.slideSword.x < -100 && !player.isSliding) { self.currentState = self.states.COMPLETE; self.setMessage("Tutorial Complete!"); LK.setTimeout(function () { self.complete(); }, 2000); } break;
User prompt
Update as needed with: if (self.currentState === self.states.SLIDE) { // Only spawn sword if player is on ground and previous sword is gone if (!self.slideSword && player.isOnGround && !player.isJumping) { self.slideSword = new SkeletonSword(); self.slideSword.x = GAME_WIDTH + 100; self.slideSword.y = player.y - 120; game.addChild(self.slideSword); }
Code edit (5 edits merged)
Please save this source code
User prompt
In the tutorial we need to combine the two slide state completion sections
Code edit (1 edits merged)
Please save this source code
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Arrow = Container.expand(function () {
	var self = Container.call(this);
	self.sprite = self.attachAsset('arrow', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 25;
	self.damage = 1;
	self.update = function () {
		self.x += self.speed;
		// Destroy if off screen
		if (self.x > GAME_WIDTH + 100) {
			self.destroy();
		}
	};
	self.getBounds = function () {
		return {
			left: self.x - 40,
			right: self.x + 40,
			top: self.y - 10,
			bottom: self.y + 10
		};
	};
	self.updateAmmo = function (amount) {
		self.ammoText.setText('x' + amount);
		// Adjust position based on digit count
		if (amount >= 10) {
			self.ammoText.x = -150;
		} else {
			self.ammoText.x = -100;
		}
	};
	return self;
});
var ArrowPickup = Container.expand(function () {
	var self = Container.call(this);
	self.sprite = self.attachAsset('arrow', {
		anchorX: 0.2,
		anchorY: 0.5,
		scaleX: 1.2,
		scaleY: 1.2
	});
	// Initialize physics properties
	CollectibleBehavior.initPhysics(self);
	self.collect = function () {
		if (!self.collected) {
			self.collected = true;
			player.ammoCount += 1;
			scoreManager.updateAmmo(player.ammoCount);
			LK.getSound('arrowpickup').play();
			// Create arrow popup
			var popup = LK.getAsset('arrow', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 1.3,
				scaleY: 1.3
			});
			popup.x = self.x - 50;
			popup.y = self.y - 200;
			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);
			self.destroy();
		}
	};
	self.update = function () {
		CollectibleBehavior.standardUpdate(self);
	};
	return self;
});
// 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;
	// Loot modifier - skeletons get 1.5x
	self.lootModifier = self.type === 'skeleton' ? 1.6 : 1.0;
	self.maxThrowBack = 200;
	self.hitType = 'none'; // Can be 'none', 'attack', or 'slide'
	// Hitbox properties
	self.hitboxWidth = 200;
	self.hitboxHeight = self.type === 'eyeball' ? 90 : 260;
	// Animation properties
	self.sprites = [];
	self.animationCounter = 0;
	self.animationSpeed = 0.08;
	// Define all animation arrays 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'];
	} 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'];
	} else if (self.type === 'skeleton') {
		// Skeleton-specific properties
		self.runFrame = 0;
		self.speed = 6; // Slower than goblin
		// Throwing properties
		self.isThrowing = false;
		self.throwingTimer = 0;
		self.throwingDuration = 90;
		self.throwingPauseTime = 15;
		self.throwingFrame = 0;
		// Animation frames
		self.runAnimation = ['skeletonwalk1', 'skeletonwalk2', 'skeletonwalk3', 'skeletonwalk4'];
		self.hitAnimation = ['skeletonhit1', 'skeletonhit2'];
		self.dieAnimation = ['skeletondie1', 'skeletondie2', 'skeletondie3', 'skeletondie4'];
		self.throwingAnimation = ['skeletonthrow1', 'skeletonthrow2', 'skeletonthrow3', 'skeletonthrow4', 'skeletonthrow5', 'skeletonthrow6'];
	}
	// 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);
		}
	};
	// Initialize animation sprites for skeleton
	self.initSkeletonSprites = function () {
		// Add walk 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);
		}
		// Add throwing animations
		for (var i = 0; i < self.throwingAnimation.length; i++) {
			var sprite = self.attachAsset(self.throwingAnimation[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 - fixed to allow falling without platform collision
	self.updateEyeballDamageState = function () {
		if (self.isHit) {
			var hitOffset = self.flyAnimation.length;
			self.throwBackDistance += Math.abs(self.throwBackSpeed);
			self.x += self.throwBackSpeed;
			// Apply vertical velocity without any dampening on the first few frames
			// to ensure the eyeball gets significant vertical movement
			if (self.hitTimer > 30) {
				// Apply full vertical movement without reduction for initial frames
				self.y += self.velocityY;
			} else {
				// After initial frames, gradually reduce the vertical movement
				self.y += self.velocityY;
				self.velocityY *= 0.8; // Less aggressive dampening to maintain vertical motion
			}
			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;
				if (self.hitType === 'attack') {
					// Only start dying if it was a regular attack
					self.isDying = true;
					self.deathTimer = 180;
					self.deathFrame = 0;
					// Add dropLoot here when transitioning to dying state
					self.dropLoot();
				}
				self.hitType = 'none';
			}
		} else if (self.isDying) {
			var dieOffset = self.flyAnimation.length + self.hitAnimation.length;
			// Apply gravity without platform collision - this ensures constant falling
			self.velocityY += 0.5;
			self.y += self.velocityY;
			// Keep moving horizontally during fall
			self.x -= PLATFORM_SPEED * 0.5;
			// 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 * gameSpeedMultiplier;
		// 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;
		// Check if still 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;
					}
				}
			}
		}
		// Apply gravity if not on ground
		if (!self.isOnGround) {
			self.velocityY += 0.7;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// Show hit animation
		var hitOffset = self.runAnimation.length;
		self.sprites[hitOffset].alpha = 1;
		// Decrease hit timer
		self.hitTimer--;
		// Once hit timer expires, check hit type
		if (self.hitTimer <= 0) {
			self.isHit = false;
			if (self.hitType === 'attack') {
				self.isDying = true;
				self.deathTimer = 60;
				self.deathFrame = 0;
				self.dropLoot();
			}
			self.hitType = 'none';
		}
	};
	// Update skeleton hit animation
	self.updateSkeletonHitState = function () {
		// Handle throw back motion
		self.x += self.throwBackSpeed;
		self.throwBackDistance += Math.abs(self.throwBackSpeed);
		self.throwBackSpeed *= 0.95;
		// Check if still 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;
					}
				}
			}
		}
		// Apply gravity if not on ground
		if (!self.isOnGround) {
			self.velocityY += 0.7;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// Show hit animation
		var hitOffset = self.runAnimation.length;
		var hitFrame = Math.floor(self.hitTimer / 18) % 2; // Alternate between hit frames
		self.sprites[hitOffset + hitFrame].alpha = 1;
		// Decrease hit timer
		self.hitTimer--;
		// Once hit timer expires, check hit type
		if (self.hitTimer <= 0) {
			self.isHit = false;
			if (self.hitType === 'attack') {
				self.isDying = true;
				self.deathTimer = 60;
				self.deathFrame = 0;
				self.dropLoot();
			}
			self.hitType = 'none';
		}
	};
	// Update goblin dying animation
	self.updateGoblinDyingState = function () {
		// Continue throw back during death
		self.x += self.throwBackSpeed;
		// Check if still 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;
					}
				}
			}
		}
		// Apply gravity if not on ground
		if (!self.isOnGround) {
			self.velocityY += 0.7;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// 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 skeleton dying animation
	self.updateSkeletonDyingState = function () {
		// Continue throw back during death
		self.x += self.throwBackSpeed;
		// Check if still 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;
					}
				}
			}
		}
		// Apply gravity if not on ground
		if (!self.isOnGround) {
			self.velocityY += 0.7;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// 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 with speed multiplier
		self.x -= self.speed * gameSpeedMultiplier;
		// 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;
	};
	// Update skeleton throwing animation
	self.updateSkeletonThrowingState = function () {
		// Always move exactly with platform speed during throwing
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		// Offset to find throwing animation sprites
		var throwOffset = self.runAnimation.length + self.hitAnimation.length + self.dieAnimation.length;
		// Progress through throwing frames
		if (self.throwingTimer > self.throwingDuration - self.throwingPauseTime) {
			// Early frames building up to throw
			var frameIndex = Math.floor((self.throwingDuration - self.throwingTimer) / ((self.throwingDuration - self.throwingPauseTime) / 5));
			frameIndex = Math.min(4, Math.max(0, frameIndex));
			self.sprites[throwOffset + frameIndex].alpha = 1;
		} else if (self.throwingTimer === Math.floor(self.throwingDuration * 0.8)) {
			var sword = new SkeletonSword();
			sword.x = self.x - 100;
			sword.y = self.y - 100;
			game.addChild(sword);
			skeletonSwords.push(sword);
			self.sprites[throwOffset + 5].alpha = 1;
		} else if (self.throwingTimer <= self.throwingPauseTime) {
			// During pause, keep showing the final frame
			self.sprites[throwOffset + 5].alpha = 1;
		} else {
			// Middle of animation (after throw, before pause)
			self.sprites[throwOffset + 5].alpha = 1;
		}
		// Decrease timer
		self.throwingTimer--;
		// End throwing state and restore speed
		if (self.throwingTimer <= 0) {
			self.isThrowing = false;
			self.speed = self.originalSpeed || 6;
		}
	};
	// Update skeleton normal movement
	self.updateSkeletonNormalState = function () {
		// Track if this is the first time we're seeing this skeleton
		if (self.initialThrowMade === undefined) {
			self.initialThrowMade = false;
			self.enteredScreenTime = LK.ticks;
			self.throwDelay = 60; // Reduce delay before first throw
		}
		// Check for initial throw
		if (!self.initialThrowMade && self.x < GAME_WIDTH + 300 && self.isOnGround && !self.isThrowing) {
			if (LK.ticks - self.enteredScreenTime > self.throwDelay) {
				self.isThrowing = true;
				self.throwingTimer = self.throwingDuration;
				self.throwingFrame = 0;
				self.originalSpeed = self.speed;
				self.initialThrowMade = true;
			}
		}
		// Continue with normal movement regardless of throw state
		// Move left with speed multiplier
		self.x -= self.speed * gameSpeedMultiplier;
		// Platform and gravity code
		if (!self.isOnGround) {
			self.velocityY += 0.7;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// Ensure skeleton 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 walking - slower animation speed for skeleton
		self.animationCounter += self.animationSpeed * 0.8;
		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
		};
	};
	self.slideHit = function () {
		if (!self.isHit && !self.isDying) {
			self.isHit = true;
			self.hitType = 'slide'; // Specify this is a slide hit
			self.throwBackSpeed = 25;
			self.throwBackDistance = 0;
			self.hitTimer = 30;
			// Add vertical trajectory for eyeball hits based on Y position comparison with player
			var verticalTrajectory;
			if (self.type === 'eyeball') {
				var yDifference = self.y - player.y;
				var heightThreshold = 100; // Adjust this value to control the size of the middle zone
				if (yDifference < -heightThreshold) {
					// Eyeball is well above player, push upward strongly
					verticalTrajectory = -20;
				} else if (yDifference > heightThreshold) {
					// Eyeball is well below player, push downward strongly
					verticalTrajectory = 14;
				} else {
					// Eyeball is in middle zone, small upward push
					verticalTrajectory = -2;
				}
				// Immediately apply the full vertical trajectory when hit with no reduction
				self.velocityY = verticalTrajectory; // Set the velocity for the eyeball
			}
			// Add particle effect
			var particleOffset = self.type === 'eyeball' ? 175 : 250;
			particleSystem.emitFromHit(self.x + particleOffset, self.y, player.x, verticalTrajectory, self.type);
		}
	};
	// Hit handling
	self.hit = function () {
		if (!self.isHit && !self.isDying) {
			self.isHit = true;
			self.hitType = 'attack'; // Specify this is a regular attack hit
			self.throwBackSpeed = 25;
			self.throwBackDistance = 0;
			self.hitTimer = 35;
			// Calculate vertical trajectory for eyeball hits based on Y position comparison with player
			var verticalTrajectory;
			if (self.type === 'eyeball') {
				var yDifference = self.y - player.y;
				var heightThreshold = 100; // Adjust this value to control the size of the middle zone
				if (yDifference < -heightThreshold) {
					// Eyeball is well above player, push upward strongly
					verticalTrajectory = -20;
				} else if (yDifference > heightThreshold) {
					// Eyeball is well below player, push downward strongly
					verticalTrajectory = 14;
				} else {
					// Eyeball is in middle zone, small upward push
					verticalTrajectory = -2;
				}
				// Immediately apply the vertical trajectory with stronger values
				self.velocityY = verticalTrajectory; // Set the velocity for the eyeball
			} else {
				self.velocityY = 0; // Reset vertical velocity for new hit for non-eyeball enemies
			}
			// Add particle effect 
			var particleOffset = self.type === 'eyeball' ? 175 : 250;
			particleSystem.emitFromHit(self.x + particleOffset, self.y, player.x, verticalTrajectory, self.type);
		}
	};
	self.dropLoot = function () {
		// Don't drop collectibles during tutorial
		if (tutorialActive) {
			return;
		}
		// Determine number of coins to drop (1-5) adjusted by loot modifier
		var coinCount = Math.floor(GameUtils.randomRange(1, 6) * self.lootModifier);
		// Small chance for jewels (2% each) - increased by loot modifier
		var dropJewel = Math.random();
		var jewelChance = 0.02 * self.lootModifier; // Base 2% chance multiplied by modifier
		if (dropJewel < jewelChance) {
			// Diamond (rarest)
			var diamond = new Coin('diamond');
			diamond.x = self.x;
			diamond.y = self.y;
			diamond.velocityX = GameUtils.randomRange(-2, 2); // Reduced horizontal velocity
			diamond.velocityY = -GameUtils.randomRange(6, 10); // Reduced vertical velocity
			game.addChild(diamond);
			coins.push(diamond);
		} else if (dropJewel < jewelChance * 2) {
			// Emerald
			var emerald = new Coin('emerald');
			emerald.x = self.x;
			emerald.y = self.y;
			emerald.velocityX = GameUtils.randomRange(-2, 2); // Reduced horizontal velocity
			emerald.velocityY = -GameUtils.randomRange(6, 10); // Reduced vertical velocity
			game.addChild(emerald);
			coins.push(emerald);
		} else if (dropJewel < jewelChance * 3) {
			// Ruby
			var ruby = new Coin('ruby');
			ruby.x = self.x;
			ruby.y = self.y;
			ruby.velocityX = GameUtils.randomRange(-2, 2); // Reduced horizontal velocity
			ruby.velocityY = -GameUtils.randomRange(6, 10); // Reduced vertical velocity
			game.addChild(ruby);
			coins.push(ruby);
		}
		// Drop regular coins
		for (var i = 0; i < coinCount; i++) {
			var coin = new Coin();
			coin.x = self.x;
			coin.y = self.y;
			coin.velocityX = GameUtils.randomRange(-2, 2); // Reduced horizontal velocity
			coin.velocityY = -GameUtils.randomRange(6, 10); // Reduced vertical velocity
			game.addChild(coin);
			coins.push(coin);
		}
	};
	self.isDeathAnimationComplete = function () {
		return self.isDying && self.deathTimer <= 10; // Check if we're near the end of death animation
	};
	// 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 if (self.type === 'skeleton') {
			// Skeleton logic
			if (self.isHit) {
				self.updateSkeletonHitState();
			} else if (self.isDying) {
				self.updateSkeletonDyingState();
			} else if (self.isThrowing) {
				self.updateSkeletonThrowingState();
			} else {
				self.updateSkeletonNormalState();
			}
		} 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();
		}
	};
	// Initialize based on enemy type AFTER defining the methods
	if (self.type === 'eyeball') {
		self.initEyeballSprites();
	} else if (self.type === 'goblin') {
		self.initGoblinSprites();
	} else if (self.type === 'skeleton') {
		self.initSkeletonSprites();
	} else {
		// Basic enemy
		var enemyGraphics = self.attachAsset('enemy', {
			anchorX: 0.5,
			anchorY: 0.5
		});
	}
	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 = LK.getAsset('heart', {
				anchorX: 0.5,
				anchorY: 0.5,
				tint: 0xFF0000
			});
			popup.scaleX = 1.6; // Adjust scale to match original size
			popup.scaleY = 1.6;
			popup.x = self.x - 50;
			popup.y = self.y - 200;
			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(2, 6); // Reduced horizontal velocity
				potion.velocityY = -GameUtils.randomRange(8, 14); // Reduced vertical velocity
				game.addChild(potion);
				coins.push(potion);
			}
			// Add chance for arrow (10%)
			if (Math.random() < 0.25) {
				var arrow = new ArrowPickup();
				arrow.x = jar.x;
				arrow.y = jar.y;
				arrow.velocityX = GameUtils.randomRange(2, 6);
				arrow.velocityY = -GameUtils.randomRange(8, 14);
				// Set rotation to 90 degrees counterclockwise
				arrow.sprite.rotation = -Math.PI / 2;
				arrow.platformOffset = 200;
				game.addChild(arrow);
				coins.push(arrow);
			}
			// 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(2, 6); // Reduced horizontal velocity
				coin.velocityY = -GameUtils.randomRange(8, 14); // Reduced vertical velocity
				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, verticalTrajectory, enemyType) {
		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;
			// Only apply red tint if not skeleton enemy type
			if (enemyType !== 'skeleton') {
				particle.tint = self.redTints[Math.floor(Math.random() * self.redTints.length)];
			} else {
				particle.tint = 0xFFFFFF; // White particles for skeleton
			}
			// 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;
			// Use provided vertical trajectory if available, otherwise use default calculation
			if (verticalTrajectory !== undefined) {
				// Add some randomness while maintaining the general direction
				var randomFactor = Math.random() * 0.5 + 0.75; // 0.75 to 1.25 range
				particle.velocityY = verticalTrajectory * randomFactor;
			} else {
				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 -= PLATFORM_SPEED * gameSpeedMultiplier;
		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'];
	self.airAttackAnimation = ['playerairattack1', 'playerairattack2', 'playerairattack3', 'playerairattack4'];
	self.slideAnimation = ['playerslide1', 'playerslide2'];
	self.standUpAnimation = ['playerstand1', 'playerstand2', 'playerstand3'];
	self.deathAnimation = ['playerdie1', 'playerdie2', 'playerdie3', 'playerdie4', 'playerdie5', 'playerdie6', 'playerdie7'];
	self.bowAnimation = ['playerbow4', 'playerbow5', 'playerbow6', 'playerbow7', 'playerbow8', 'playerbow9'];
	// Bow properties
	self.isShooting = false;
	self.bowFrame = 0;
	self.bowCooldown = 20; // Frames of cooldown
	self.bowCooldownTimer = 0;
	self.ammoCount = 5;
	// Animation states
	self.isAttacking = false;
	self.attackFrame = 0;
	self.runFrame = 0;
	self.animationSpeed = 0.08;
	self.attackAnimationSpeed = 0.15;
	self.animationCounter = 0;
	self.sprites = [];
	// Death animation properties
	self.isDying = false;
	self.deathFrame = 0;
	self.deathTimer = 0;
	self.deathDuration = 100;
	self.deathAnimationSpeed = 0.1;
	// 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;
	self.isSliding = false;
	self.slideTimer = 0;
	self.slideDuration = 90;
	self.standUpDuration = 30;
	self.slideCooldown = 30;
	self.lastSlideTime = 0;
	self.slideSpeedMultiplier = 1.75;
	self.normalHitboxHeight = self.hitboxHeight; // Store original height
	self.slideHitboxHeight = 200; // Make it taller
	self.slideHitboxYOffset = 150; // Move it much lower
	// 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);
		}
		// Air attack animations
		for (var i = 0; i < self.airAttackAnimation.length; i++) {
			var sprite = self.attachAsset(self.airAttackAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Add slide animations
		for (var i = 0; i < self.slideAnimation.length; i++) {
			var sprite = self.attachAsset(self.slideAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Add stand up animations
		for (var i = 0; i < self.standUpAnimation.length; i++) {
			var sprite = self.attachAsset(self.standUpAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Add death animations
		for (var i = 0; i < self.deathAnimation.length; i++) {
			var sprite = self.attachAsset(self.deathAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Add bow animations
		for (var i = 0; i < self.bowAnimation.length; i++) {
			var sprite = self.attachAsset(self.bowAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
	};
	// Call initialization
	self.initAnimations();
	// Get collision bounds
	self.getBounds = function () {
		// For collecting items, always use the full-size hitbox
		return {
			left: self.x - self.hitboxWidth / 2,
			right: self.x + self.hitboxWidth / 2,
			top: self.y - self.normalHitboxHeight / 2,
			bottom: self.y + self.normalHitboxHeight / 2
		};
	};
	// Get attack hitbox
	self.getAttackBounds = function () {
		if (!self.isAttacking || self.isSliding) {
			// Add sliding check here 
			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
		};
	};
	self.getSlideAttackBounds = function () {
		if (!self.isSliding) {
			return null;
		}
		return {
			left: self.x + self.hitboxWidth / 2,
			// Extend hitbox forward during slide
			right: self.x + self.hitboxWidth / 2 + 100,
			// Adjust width as needed
			top: self.y - self.slideHitboxHeight / 2 + self.slideHitboxYOffset,
			bottom: self.y + self.slideHitboxHeight / 2 + self.slideHitboxYOffset
		};
	};
	self.getCollisionBounds = function () {
		if (self.isSliding) {
			return {
				left: self.x - self.hitboxWidth / 2,
				right: self.x + self.hitboxWidth / 2,
				top: self.y - self.slideHitboxHeight / 2 + self.slideHitboxYOffset,
				bottom: self.y + self.slideHitboxHeight / 2 + self.slideHitboxYOffset
			};
		}
		return self.getBounds();
	};
	// 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 () {
		// 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 + 20;
			var rightEdge = platform.x + PLATFORM_HALF_WIDTH + 20;
			// 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;
			}
		}
		// 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;
				}
			}
		}
	};
	// Apply physics (separated from animation) - fixed to check collisions for ALL falling cases
	self.applyPhysics = function () {
		// Apply gravity and velocity to position
		if (!self.isOnGround || self.isJumping) {
			// Apply velocity and gravity
			self.y += self.velocityY;
			self.velocityY += 0.7;
			// Check for landing on platforms
			var hitPlatform = self.checkPlatformCollision();
			// REMOVE THE GROUND CHECK - No invisible ground at the bottom!
			// We only want to land on actual platforms
		}
		// Check for falling off screen - but instead of immediate game over,
		// let player continue to fall, then trigger game over after 3 seconds
		if (self.y > GAME_HEIGHT && !self.isDying && !self.dyingInAir) {
			// Start death animation instead of immediately showing game over
			self.isDying = true;
			self.deathTimer = 0;
			self.deathFrame = 0;
			// Set game speed multiplier to 0 when player dies
			gameSpeedMultiplier = 0;
			// Stop background music and play gameover sound
			LK.stopMusic();
			LK.getSound('gameover').play();
			// Flash screen
			LK.effects.flashScreen(0xff0000, 1000);
			// Schedule game over call after 3 seconds
			LK.setTimeout(function () {
				LK.showGameOver();
			}, 3000);
		}
	};
	// 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 air attack animation
	self.updateAirAttackAnimation = function () {
		var airAttackOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length;
		self.animationCounter += self.attackAnimationSpeed;
		if (self.animationCounter >= 1) {
			self.animationCounter = 0;
			self.attackFrame++;
			if (self.attackFrame >= self.airAttackAnimation.length) {
				self.isAttacking = false;
				self.attackFrame = 0;
			}
		}
		self.sprites[airAttackOffset + self.attackFrame].alpha = 1;
	};
	// Update jump animation (only animation, not physics)
	self.updateJumpAnimation = function () {
		var jumpOffset = self.runAnimation.length;
		var currentTime = Date.now();
		// 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 slide animation
	self.updateSlideAnimation = function () {
		var slideOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length;
		var standUpOffset = slideOffset + self.slideAnimation.length;
		self.slideTimer--;
		if (self.slideTimer > self.standUpDuration) {
			// Main slide animation (alternate between first two frames)
			self.animationCounter += self.animationSpeed;
			if (self.animationCounter >= 1) {
				self.animationCounter = 0;
			}
			var slideFrame = Math.floor(self.animationCounter * 2);
			self.sprites[slideOffset + slideFrame].alpha = 1;
		} else if (self.slideTimer > 0) {
			// Stand up animation
			var standUpFrame = Math.floor((self.standUpDuration - self.slideTimer) / (self.standUpDuration / self.standUpAnimation.length));
			self.sprites[standUpOffset + standUpFrame].alpha = 1;
		} else {
			// End slide
			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;
			}
		}
	};
	self.updateBowAnimation = function () {
		var bowOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length + self.deathAnimation.length;
		self.animationCounter += self.attackAnimationSpeed;
		if (self.animationCounter >= 1) {
			self.animationCounter = 0;
			self.bowFrame++;
			// Spawn arrow at frame 5
			if (self.bowFrame === 5) {
				// If in tutorial, always spawn arrow
				if (tutorialActive) {
					var arrow = new Arrow();
					arrow.x = self.x + 100;
					arrow.y = self.y + 20;
					game.addChild(arrow);
					arrows.push(arrow);
					LK.getSound('arrowfire').play();
				} else if (self.ammoCount > 0) {
					var arrow = new Arrow();
					arrow.x = self.x + 100;
					arrow.y = self.y + 20;
					game.addChild(arrow);
					arrows.push(arrow);
					LK.getSound('arrowfire').play();
				} else {
					// Play bow fire sound effect when no ammo
					LK.getSound('bowfiring').play();
				}
			}
			if (self.bowFrame >= self.bowAnimation.length) {
				self.isShooting = false;
				self.bowFrame = 0;
				self.bowCooldownTimer = self.bowCooldown;
			}
		}
		self.sprites[bowOffset + self.bowFrame].alpha = 1;
	};
	// Update death animation
	self.updateDeathAnimation = function () {
		// Calculate offset to find the death animation sprites
		var deathOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length;
		// Progress timer
		self.deathTimer++;
		// Calculate frame based on timer
		// We want the animation to play through all frames over the death duration
		var frameProgress = self.deathTimer / self.deathDuration * self.deathAnimation.length;
		var newFrame = Math.min(Math.floor(frameProgress), self.deathAnimation.length - 1);
		// Update frame if it's changed
		if (newFrame != self.deathFrame) {
			self.deathFrame = newFrame;
		}
		// Show current frame
		self.sprites[deathOffset + self.deathFrame].alpha = 1;
		// Death animation is complete, but game over is handled by the timeout
		if (self.deathTimer >= self.deathDuration) {
			// Game over is now triggered by the timeout in takeDamage
		}
	};
	// 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;
	};
	self.checkPlatformBelow = function () {
		if (!self.currentPlatform) {
			return null;
		}
		// Get current platform height
		var currentHeight = self.currentPlatform.y;
		// Find the closest platform below current one
		var platformBelow = null;
		var minDistance = Infinity;
		for (var i = 0; i < platforms.length; i++) {
			var platform = platforms[i];
			// Skip if it's not below us or if it's our current platform
			if (platform === self.currentPlatform || platform.y <= currentHeight) {
				continue;
			}
			// Check if we're within platform width bounds
			if (Math.abs(platform.x - self.x) < PLATFORM_HALF_WIDTH) {
				var distance = platform.y - currentHeight;
				if (distance < minDistance) {
					minDistance = distance;
					platformBelow = platform;
				}
			}
		}
		return platformBelow;
	};
	// Check platform collision - fix to properly detect platforms when falling
	self.checkPlatformCollision = function () {
		for (var i = 0; i < platforms.length; i++) {
			var platform = platforms[i];
			// Increase vertical tolerance from 15 to 20 pixels
			// Add larger horizontal buffer (30 pixels) on each side for more forgiving landings
			if (self.velocityY > 0 && self.y < platform.y - PLAYER_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - PLAYER_PLATFORM_OFFSET - 20 && self.x > platform.x - (PLATFORM_HALF_WIDTH + 60) && self.x < platform.x + (PLATFORM_HALF_WIDTH + 60)) {
				// Snap to platform more precisely
				self.y = platform.y - PLAYER_PLATFORM_OFFSET;
				self.velocityY = 0;
				self.isJumping = false;
				self.isOnGround = true;
				self.currentPlatform = platform;
				return true;
			}
		}
		return false;
	};
	// Hide all sprites
	self.hideAllSprites = function () {
		for (var i = 0; i < self.sprites.length; i++) {
			self.sprites[i].alpha = 0;
		}
	};
	self.checkCollectibles = function () {
		// Always check for collectibles regardless of state
		for (var i = 0; i < coins.length; i++) {
			var coin = coins[i];
			var itemBounds = {
				left: coin.x - 25,
				right: coin.x + 25,
				top: coin.y - 25,
				bottom: coin.y + 25
			};
			if (GameUtils.checkCollision(self.getBounds(), itemBounds)) {
				coin.collect();
			}
		}
	};
	// Jump method
	self.jump = function () {
		if (self.isSliding || self.isShooting && self.bowFrame < 2) {
			// Only prevent jumps at start of bow animation
			return;
		}
		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;
			if (tutorialManager && tutorialManager.currentState === tutorialManager.states.DOUBLE_JUMP) {
				tutorialManager.firstJumpPerformed = true;
			}
		} else if (self.isJumping && self.velocityY < 10) {
			// Small double-jump to reach higher platforms
			self.velocityY = -self.jumpHeight * 0.7;
			self.jumpStartTime = Date.now();
			if (tutorialManager && tutorialManager.currentState === tutorialManager.states.DOUBLE_JUMP) {
				tutorialManager.secondJumpPerformed = true;
			}
		}
	};
	// Add slide method
	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;
		}
	};
	// Attack method
	self.attack = function () {
		if (!self.isAttacking && !self.isSliding) {
			// Add sliding check here
			self.isAttacking = true;
			self.attackFrame = 0;
			self.animationCounter = 0;
			LK.getSound('swordslash').play();
		}
	};
	self.fallThrough = function () {
		if (self.isOnGround && !self.isJumping && !self.isSliding && !self.isAttacking) {
			var platformBelow = self.checkPlatformBelow();
			if (platformBelow) {
				// Disable platform collision temporarily
				self.isOnGround = false;
				self.currentPlatform = null;
				self.velocityY = 5; // Start with a small downward velocity
				// Re-enable platform collision after a short delay
				LK.setTimeout(function () {
					self.checkPlatformCollision = self.originalCheckPlatformCollision;
				}, 250); // Adjust timing as needed
				// Store the original collision check and temporarily disable it
				self.originalCheckPlatformCollision = self.checkPlatformCollision;
				self.checkPlatformCollision = function () {
					return null;
				};
			}
		}
	};
	self.shoot = function () {
		// If in tutorial, only allow shooting during bow phase
		if (tutorialActive && tutorialManager.currentState !== tutorialManager.states.BOW) {
			return;
		}
		if (!self.isShooting && !self.isSliding && self.bowCooldownTimer <= 0) {
			if (self.isOnGround || !self.isOnGround) {
				// Allow shooting in air or ground
				self.isShooting = true;
				self.bowFrame = 0;
				self.animationCounter = 0;
				// If in tutorial, don't consume ammo
				if (!tutorialActive) {
					if (self.ammoCount <= 0) {
						// Play bow fire sound effect when no ammo
						LK.getSound('bowfiring').play();
						return;
					}
					self.ammoCount--;
					scoreManager.updateAmmo(self.ammoCount);
				}
				// If in air, pause vertical movement
				if (!self.isOnGround) {
					self.velocityY = 0;
				}
			}
		}
	};
	// Take damage method
	self.takeDamage = function () {
		if (!self.isInvulnerable && !self.isDying) {
			LK.getSound('playerouch').play();
			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
			});
			// Add red screen flash
			LK.effects.flashScreen(0xff0000, 300);
			// Add screen shake effect
			applyScreenShake();
			if (self.currentHealth <= 0) {
				// If in the air, find the nearest platform to fall to
				if (!self.isOnGround) {
					// Start the dying sequence but let them fall first
					self.dyingInAir = true;
					// Find the closest platform below the player
					var closestPlatform = null;
					var closestDistance = Infinity;
					for (var i = 0; i < platforms.length; i++) {
						var platform = platforms[i];
						// Check if platform is below player and within horizontal range
						if (platform.y > self.y && Math.abs(platform.x - self.x) < PLATFORM_HALF_WIDTH) {
							var distance = platform.y - self.y;
							if (distance < closestDistance) {
								closestDistance = distance;
								closestPlatform = platform;
							}
						}
					}
					// If we found a platform, let player fall to it naturally
					// Otherwise proceed with normal death sequence
					if (!closestPlatform) {
						startDeathSequence();
					}
					// Player will continue falling until they hit a platform or go off screen
					// The death sequence will be triggered in the update method
				} else {
					// Start death sequence immediately if already on ground
					startDeathSequence();
				}
			}
		}
		function startDeathSequence() {
			// Start death animation
			self.isDying = true;
			self.deathTimer = 0;
			self.deathFrame = 0;
			// Set game speed multiplier to 0 when player dies
			gameSpeedMultiplier = 0;
			// Stop background music and play gameover sound
			LK.stopMusic();
			LK.getSound('gameover').play();
			// Flash screen
			LK.effects.flashScreen(0xff0000, 1000);
			// Apply one final screen shake for dramatic effect
			applyScreenShake();
			// Schedule game over call after 2 seconds
			LK.setTimeout(function () {
				LK.showGameOver();
			}, 2000);
		}
	};
	// Main update method
	self.update = function () {
		// Update heart container
		self.updateHeartContainer();
		// Hide all sprites
		self.hideAllSprites();
		// If player is dying but still in the air, apply physics to let them fall
		if (self.dyingInAir) {
			// Apply physics for falling
			self.applyPhysics();
			// Check if they've landed on a platform
			if (self.isOnGround) {
				// They've landed, now start the death sequence
				self.dyingInAir = false;
				self.isDying = true;
				self.deathTimer = 0;
				self.deathFrame = 0;
				gameSpeedMultiplier = 0;
				LK.stopMusic();
				LK.getSound('gameover').play();
				LK.effects.flashScreen(0xff0000, 1000);
				applyScreenShake();
				LK.setTimeout(function () {
					LK.showGameOver();
				}, 2000);
			}
			// Show falling animation
			var jumpOffset = self.runAnimation.length;
			self.sprites[jumpOffset + 2].alpha = 1; // Show falling frame
			return;
		}
		// If player is dying normally, only update death animation
		if (self.isDying) {
			self.updateDeathAnimation();
			return;
		}
		// Update bow cooldown
		if (self.bowCooldownTimer > 0) {
			self.bowCooldownTimer--;
		}
		// Apply physics FIRST - this now happens regardless of animation state
		self.applyPhysics();
		// Handle platform collision and falling
		self.handlePlatformCollision();
		// Handle invulnerability
		self.updateInvulnerability();
		// Update animations based on state
		if (self.isShooting) {
			self.updateBowAnimation();
		} else if (self.isSliding) {
			self.updateSlideAnimation();
			self.checkCollectibles(); // Explicitly check during slide
		} else if (self.isAttacking) {
			if (self.isJumping || !self.isOnGround && !self.isSliding) {
				self.updateAirAttackAnimation();
			} else {
				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 && !self.isDying) {
			// Start death animation instead of immediately showing game over
			self.isDying = true;
			self.deathTimer = 0;
			self.deathFrame = 0;
			LK.effects.flashScreen(0xff0000, 1000);
		}
	};
	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;
});
var SkeletonSword = Container.expand(function () {
	var self = Container.call(this);
	// Attach the sword sprite
	self.sprite = self.attachAsset('skeletonsword', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Set properties
	self.speed = 6; // Slightly slower than arrows (25)
	self.rotationSpeed = -0.1; // Counterclockwise rotation
	// Update method called every frame
	self.update = function () {
		// Move from right to left with speed adjusted by game speed multiplier
		self.x -= self.speed * gameSpeedMultiplier;
		// Rotate counterclockwise
		self.sprite.rotation += self.rotationSpeed;
		// Destroy if off screen
		if (self.x < -100) {
			self.destroy();
		}
	};
	// Get bounds for collision detection
	self.getBounds = function () {
		return {
			left: self.x - 40,
			right: self.x + 40,
			top: self.y - 40,
			bottom: self.y + 40
		};
	};
	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(1, 6); // Reduced horizontal velocity
				potion.velocityY = -GameUtils.randomRange(8, 16); // Reduced vertical velocity
				game.addChild(potion);
				coins.push(potion);
			}
			// Higher chance for arrows (25%)
			if (Math.random() < 0.40) {
				var arrowCount = Math.floor(GameUtils.randomRange(1, 4));
				for (var i = 0; i < arrowCount; i++) {
					var arrow = new ArrowPickup();
					arrow.x = chest.x;
					arrow.y = chest.y;
					arrow.velocityX = GameUtils.randomRange(1, 6);
					arrow.velocityY = -GameUtils.randomRange(8, 16);
					// Set rotation to 90 degrees counterclockwise
					arrow.sprite.rotation = -Math.PI / 2;
					arrow.platformOffset = 40;
					game.addChild(arrow);
					coins.push(arrow);
				}
			}
			// 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(1, 6); // Reduced horizontal velocity
				item.velocityY = -GameUtils.randomRange(8, 16); // Reduced vertical velocity
				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;
});
var TutorialManager = Container.expand(function () {
	var self = Container.call(this);
	// Tutorial states
	self.states = {
		ATTACK: 'attack',
		BOW: 'bow',
		JUMP: 'jump',
		DROP_DOWN: 'drop_down',
		DOUBLE_JUMP: 'double_jump',
		TRANSITIONING_TO_SLIDE: 'transitioning_to_slide',
		// New state
		SLIDE: 'slide',
		COMPLETE: 'complete'
	};
	// Current state and tracking
	self.currentState = self.states.ATTACK;
	self.enemyKilledByAttack = false;
	self.enemyKilledByBow = false;
	self.dropDownInput = false;
	self.hasJumped = false;
	self.hasDoubleJumped = false;
	self.firstJumpPerformed = false;
	self.secondJumpPerformed = false;
	// Message display
	self.messageContainer = new Container();
	self.messageText = new Text2('', {
		size: 100,
		fill: 0xFFFFFF,
		anchorX: 0.5,
		anchorY: 0.5,
		align: 'center',
		width: GAME_WIDTH
	});
	self.messageContainer.addChild(self.messageText);
	self.messageContainer.x = GAME_WIDTH / 2;
	self.messageContainer.y = GAME_HEIGHT / 3.2;
	self.messageText.x = 0; // Center the text in the container
	self.messageText.y = 0; // Center the text in the container
	// Initialize tutorial
	self.init = function () {
		self.slideSword = null; // Track the sword for slide phase
		self.platformRemovalTimer = 0;
		self.removingPlatforms = false;
		self.hasSlideSucceeded = false;
		// Create invulnerable player
		player = game.addChild(new Player());
		player.x = GAME_WIDTH / 4.5;
		player.y = GAME_HEIGHT / 1.5 - 100;
		player.isInvulnerable = true;
		// Reset state tracking
		self.enemyKilledByAttack = false;
		self.enemyKilledByBow = false;
		self.dropDownInput = false;
		// Set up initial platforms
		self.setupInitialPlatforms();
		// Add message container
		game.addChild(self.messageContainer);
		self.setMessage("Tap anywhere to attack. \n Kill the goblin!");
		// Add backbutton centered under the main platform
		self.backButton = self.messageContainer.attachAsset('backbutton', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		self.backButton.x = -700;
		self.backButton.y = -500; // 400px below message, visually under platform
		// Add touch handler for backbutton
		self.backButtonDown = function (x, y, obj) {
			// Only allow backbutton if tutorial is active
			if (tutorialActive && typeof self.complete === "function") {
				self.complete();
			}
		};
		self.backButton.down = self.backButtonDown;
		// Clear any existing enemies/arrows
		enemies = [];
		arrows = [];
		self.spawnTutorialEnemy();
	};
	// Message handling
	self.setMessage = function (text) {
		self.messageText.setText(text);
		// Calculate width of text and offset by half to center it
		var textWidth = self.messageText.width;
		self.messageText.x = -textWidth / 2;
		self.messageText.y = 0;
	};
	// Platform setup and management
	self.setupInitialPlatforms = function () {
		for (var i = 0; i < 5; i++) {
			var platform = new Platform();
			platform.x = i * (PLATFORM_WIDTH - PLATFORM_OVERLAP);
			platform.y = lowPlatformHeight;
			platforms.push(platform);
			game.addChild(platform);
		}
	};
	self.setupJumpPlatforms = function () {
		// Clear any high platforms
		for (var i = platforms.length - 1; i >= 0; i--) {
			if (platforms[i].y < lowPlatformHeight) {
				platforms[i].destroy();
				platforms.splice(i, 1);
			}
		}
		// Create initial high platform
		var platform = new Platform();
		platform.x = GAME_WIDTH;
		platform.y = highPlatformHeight;
		platforms.push(platform);
		game.addChild(platform);
	};
	self.setupDoubleJumpPlatforms = function () {
		// Clear any existing high platforms
		for (var i = platforms.length - 1; i >= 0; i--) {
			if (platforms[i].y < lowPlatformHeight) {
				platforms[i].destroy();
				platforms.splice(i, 1);
			}
		}
		// Create initial highest platform
		var platform = new Platform();
		platform.x = GAME_WIDTH;
		platform.y = HIGHEST_PLATFORM_HEIGHT;
		platforms.push(platform);
		game.addChild(platform);
	};
	self.updateTutorialPlatforms = function () {
		// Always maintain ground level platforms
		var lastGroundPlatform = null;
		for (var i = platforms.length - 1; i >= 0; i--) {
			if (platforms[i].y === lowPlatformHeight) {
				lastGroundPlatform = platforms[i];
				break;
			}
		}
		if (lastGroundPlatform && lastGroundPlatform.x < GAME_WIDTH + 500) {
			var platform = new Platform();
			platform.x = lastGroundPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
			platform.y = lowPlatformHeight;
			platforms.push(platform);
			game.addChild(platform);
		}
		// During JUMP and DROP_DOWN phases, maintain mid height platforms
		if (self.currentState === self.states.JUMP || self.currentState === self.states.DROP_DOWN) {
			var lastMidPlatform = null;
			for (var i = platforms.length - 1; i >= 0; i--) {
				if (platforms[i].y === highPlatformHeight) {
					lastMidPlatform = platforms[i];
					break;
				}
			}
			if (lastMidPlatform && lastMidPlatform.x < GAME_WIDTH + 500) {
				var platform = new Platform();
				platform.x = lastMidPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
				platform.y = highPlatformHeight;
				platforms.push(platform);
				game.addChild(platform);
			}
		}
		// During DOUBLE_JUMP phase, maintain highest platforms
		else if (self.currentState === self.states.DOUBLE_JUMP) {
			var lastHighPlatform = null;
			for (var i = platforms.length - 1; i >= 0; i--) {
				if (platforms[i].y === HIGHEST_PLATFORM_HEIGHT) {
					lastHighPlatform = platforms[i];
					break;
				}
			}
			if (!lastHighPlatform || lastHighPlatform.x < GAME_WIDTH + 500) {
				var platform = new Platform();
				platform.x = lastHighPlatform ? lastHighPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP) : GAME_WIDTH + 100;
				platform.y = HIGHEST_PLATFORM_HEIGHT;
				platforms.push(platform);
				game.addChild(platform);
			}
		}
		// Update existing platforms
		for (var i = platforms.length - 1; i >= 0; i--) {
			platforms[i].update();
			if (platforms[i].destroyed) {
				platforms.splice(i, 1);
			}
		}
	};
	// Enemy spawning and handling
	self.spawnTutorialEnemy = function () {
		if (self.currentState === self.states.ATTACK || self.currentState === self.states.BOW) {
			// Clear existing enemies
			for (var i = enemies.length - 1; i >= 0; i--) {
				enemies[i].destroy();
			}
			enemies = [];
			var enemy = new Enemy('goblin');
			enemy.x = GAME_WIDTH + 200; // Start offscreen to the right
			enemy.y = lowPlatformHeight - ENEMY_PLATFORM_OFFSET;
			enemy.isOnGround = true;
			enemy.currentPlatform = platforms[0];
			enemies.push(enemy);
			game.addChild(enemy);
		}
	};
	// State progression check
	self.checkStateProgress = function () {
		switch (self.currentState) {
			case self.states.ATTACK:
				// Reset if enemy killed by bow
				if (self.enemyKilledByBow) {
					self.enemyKilledByBow = false;
					self.spawnTutorialEnemy();
					return;
				}
				// Progress if enemy killed by attack and death animation is nearly complete
				if (self.enemyKilledByAttack && (enemies.length === 0 || enemies[0] && enemies[0].isDeathAnimationComplete())) {
					self.enemyKilledByAttack = false;
					self.currentState = self.states.BOW;
					self.setMessage("Swipe left to fire your bow");
					self.spawnTutorialEnemy();
				}
				break;
			case self.states.BOW:
				// Reset if enemy killed by attack
				if (self.enemyKilledByAttack) {
					self.enemyKilledByAttack = false;
					self.spawnTutorialEnemy();
					return;
				}
				// Progress if enemy killed by bow and death animation is nearly complete
				if (self.enemyKilledByBow && (enemies.length === 0 || enemies[0] && enemies[0].isDeathAnimationComplete())) {
					self.enemyKilledByBow = false;
					self.currentState = self.states.JUMP;
					self.setMessage("Swipe up to jump.");
					self.setupJumpPlatforms();
				}
				break;
			case self.states.JUMP:
				if (self.hasJumped && player.isOnGround && player.currentPlatform && player.currentPlatform.y === highPlatformHeight) {
					self.currentState = self.states.DROP_DOWN;
					self.setMessage("Swipe down to drop back down.");
					self.hasJumped = false; // Reset for next use
				}
				break;
			case self.states.DROP_DOWN:
				if (self.dropDownInput && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) {
					self.currentState = self.states.DOUBLE_JUMP;
					self.setMessage("Swipe up twice for double jump.");
					self.setupDoubleJumpPlatforms();
					self.dropDownInput = false;
				}
				break;
			case self.states.DOUBLE_JUMP:
				if (self.firstJumpPerformed && self.secondJumpPerformed && player.isOnGround && player.currentPlatform && player.currentPlatform.y === HIGHEST_PLATFORM_HEIGHT && !self.removingPlatforms) {
					self.currentState = self.states.TRANSITIONING_TO_SLIDE;
					self.platformRemovalTimer = 30;
					self.removingPlatforms = true;
				}
				break;
			case self.states.TRANSITIONING_TO_SLIDE:
				if (self.platformRemovalTimer > 0) {
					self.platformRemovalTimer--;
					if (self.platformRemovalTimer === 0) {
						// Remove all high platforms after delay
						for (var i = platforms.length - 1; i >= 0; i--) {
							if (platforms[i].y < lowPlatformHeight) {
								platforms[i].destroy();
								platforms.splice(i, 1);
							}
						}
					}
				}
				// Check for transition to slide state after platforms are removed
				if (self.platformRemovalTimer === 0 && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) {
					self.currentState = self.states.SLIDE;
					self.setMessage("Swipe right to slide.\nDodge the sword.");
					self.removingPlatforms = false;
					// Reset jump tracking
					self.firstJumpPerformed = false;
					self.secondJumpPerformed = false;
				}
				break;
			case self.states.SLIDE:
				// Only spawn sword if in slide state and player is on ground
				if (!self.slideSword && player.isOnGround && player.currentPlatform && player.currentPlatform.y === lowPlatformHeight) {
					self.slideSword = new SkeletonSword();
					self.slideSword.x = GAME_WIDTH + 100;
					self.slideSword.y = player.y - 120;
					game.addChild(self.slideSword);
				}
				// Track successful slide with a new flag
				if (!self.hasSlideSucceeded && self.slideSword && self.slideSword.x < player.x && player.isSliding) {
					self.hasSlideSucceeded = true;
				}
				// Complete tutorial when player has finished sliding and succeeded
				if (self.hasSlideSucceeded && !player.isSliding) {
					self.currentState = self.states.COMPLETE;
					self.setMessage("That's it!\nNow go get some treasure!");
					LK.setTimeout(function () {
						self.complete();
					}, 3000);
				}
				break;
		}
	};
	// Input handling
	self.checkInput = function (startX, startY, endX, endY) {
		if (!gameStarted || player.isDying) {
			return;
		}
		var deltaY = endY - startY;
		var deltaX = endX - startX;
		// Ignore very small movements
		if (Math.abs(deltaY) < VERTICAL_DEADZONE && Math.abs(deltaX) < VERTICAL_DEADZONE) {
			if (self.currentState === self.states.ATTACK) {
				player.attack();
			}
			return;
		}
		// Check directional swipes
		if (Math.abs(deltaX) > Math.abs(deltaY)) {
			if (deltaX < -70 && self.currentState === self.states.BOW) {
				player.shoot();
			} else if (deltaX > 70 && self.currentState === self.states.SLIDE) {
				player.slide();
			}
		} else {
			if (deltaY < -120) {
				// Upward swipe
				if (self.currentState === self.states.JUMP) {
					self.hasJumped = true;
					player.jump();
				} else if (self.currentState === self.states.DOUBLE_JUMP) {
					self.hasDoubleJumped = true;
					player.jump();
				}
			} else if (deltaY > 120 && self.currentState === self.states.DROP_DOWN) {
				self.dropDownInput = true;
				player.fallThrough();
			}
		}
		// Additional mouse control checks like in game.move
		if (Math.abs(deltaX) > Math.abs(deltaY)) {
			if (deltaX > SLIDE_MOVE_THRESHOLD && self.currentState === self.states.SLIDE) {
				player.slide();
			} else if (deltaX < -SLIDE_MOVE_THRESHOLD && self.currentState === self.states.BOW) {
				player.shoot();
			}
		}
		// Check for downward swipe with threshold similar to game.move
		if (deltaY > SLIDE_MOVE_THRESHOLD && self.currentState === self.states.DROP_DOWN) {
			self.dropDownInput = true;
			player.fallThrough();
		}
	};
	// Main update loop
	self.update = function () {
		// Update platforms
		self.updateTutorialPlatforms();
		// Update arrows and check collisions
		for (var i = arrows.length - 1; i >= 0; i--) {
			arrows[i].update();
			if (arrows[i].destroyed) {
				arrows.splice(i, 1);
				continue;
			}
			// Check arrow collisions with enemies
			for (var j = enemies.length - 1; j >= 0; j--) {
				var enemy = enemies[j];
				if (GameUtils.checkCollision(arrows[i].getBounds(), enemy.getBounds())) {
					if (!enemy.isHit && !enemy.isDying) {
						enemy.hit();
						LK.getSound('enemyhit').play();
						arrows[i].destroy();
						arrows.splice(i, 1);
						if (self.currentState === self.states.BOW) {
							self.enemyKilledByBow = true;
						}
						break;
					}
				}
			}
		}
		// --- SLIDE SKELETON SWORD LOGIC BEGIN ---
		if (self.currentState === self.states.SLIDE) {
			// Only spawn one sword at a time
			if (!self.slideSword && player.isOnGround && !player.isJumping) {
				self.slideSword = new SkeletonSword();
				self.slideSword.x = GAME_WIDTH + 100;
				self.slideSword.y = player.y - 120;
				game.addChild(self.slideSword);
			} else if (self.slideSword) {
				// Update sword
				self.slideSword.update();
				// Check collision with player (must slide under to avoid)
				var swordBounds = self.slideSword.getBounds();
				var playerBounds = player.getCollisionBounds();
				// Only count as a hit if player is NOT sliding
				if (GameUtils.checkCollision(swordBounds, playerBounds) && !player.isSliding && !player.isDying && !player.isInvulnerable) {
					// Flash screen red when hit
					LK.effects.flashScreen(0xff0000, 300);
					// Remove sword so it can respawn
					self.slideSword.destroy();
					self.slideSword = null;
				}
				// If sword leaves screen, reset it
				if (self.slideSword && (self.slideSword.x < -100 || self.slideSword.destroyed)) {
					if (self.slideSword.parent) {
						self.slideSword.destroy();
					}
					self.slideSword = null;
				}
			}
		} else {
			// Clean up sword if not in slide state
			if (self.slideSword && self.slideSword.parent) {
				self.slideSword.destroy();
			}
			self.slideSword = null;
		}
		// --- SLIDE SKELETON SWORD LOGIC END ---
		// Update enemies and check collisions
		for (var i = enemies.length - 1; i >= 0; i--) {
			enemies[i].update();
			// Check for attack collisions
			var playerBounds = player.getCollisionBounds();
			var enemyBounds = enemies[i].getBounds();
			var attackBounds = player.getAttackBounds();
			if (attackBounds && !enemies[i].isHit && !enemies[i].isDying) {
				if (enemies[i].x > player.x && GameUtils.checkCollision(attackBounds, enemyBounds)) {
					enemies[i].hit();
					LK.getSound('enemyhit').play();
					if (self.currentState === self.states.ATTACK) {
						self.enemyKilledByAttack = true;
					} else if (self.currentState === self.states.BOW) {
						self.enemyKilledByAttack = true;
					}
					continue;
				}
			}
			// Remove destroyed enemies
			if (enemies[i].destroyed || enemies[i].x < -50) {
				enemies.splice(i, 1);
				// Only respawn if the enemy wasn't killed correctly
				if (!self.enemyKilledByAttack && !self.enemyKilledByBow) {
					self.spawnTutorialEnemy();
				}
			}
		}
		// Update particle effects
		if (particleSystem) {
			particleSystem.update();
		}
		// Check state progression
		self.checkStateProgress();
	};
	// Cleanup and completion
	self.complete = function () {
		if (self.slideSword && self.slideSword.parent) {
			self.slideSword.destroy();
			self.slideSword = null;
		}
		if (self.backButton) {
			self.backButton.destroy();
			self.backButton = null;
		}
		self.messageContainer.destroy();
		for (var i = platforms.length - 1; i >= 0; i--) {
			platforms[i].destroy();
		}
		platforms = [];
		for (var i = enemies.length - 1; i >= 0; i--) {
			enemies[i].destroy();
		}
		enemies = [];
		for (var i = arrows.length - 1; i >= 0; i--) {
			arrows[i].destroy();
		}
		arrows = [];
		if (player) {
			player.destroy();
			player = null;
		}
		createTitleScreen();
		gameStarted = false;
		tutorialActive = false;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
/**** 
* Game Variables
****/ 
// Containers
/**** 
* Game Initialization
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
/**** 
* Game Management
****/ 
// Base collectible behavior for items that can be collected
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; // Original throw physics
		self.y += self.velocityY;
		self.x -= PLATFORM_SPEED * (gameSpeedMultiplier - 1); // Only apply the extra speed from sliding
		// Check for platform collision with bounce
		if (self.velocityY > 0) {
			GameUtils.checkPlatformCollision(self, 80, true);
		}
		// Check if off screen
		if (self.x < -50 || self.y > GAME_HEIGHT) {
			self.destroy();
			return;
		}
		// Player collection detection - use slide hitbox height if sliding
		var playerBounds = {
			left: player.x - player.hitboxWidth / 2,
			right: player.x + player.hitboxWidth / 2,
			top: player.y - player.hitboxHeight / 2,
			bottom: player.y + player.hitboxHeight / 2
		};
		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 GAP_START_TIME = 90000; // 1.5 minutes in milliseconds
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 = 200;
var MOVE_THRESHOLD = 90;
var SLIDE_MOVE_THRESHOLD = 300;
var VERTICAL_DEADZONE = 30;
/**** 
* 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
	});
	// New ammo display
	self.ammoText = new Text2('0x', {
		size: 160,
		fill: 0xFFFFFF,
		anchorX: 1,
		anchorY: 0.5
	});
	self.ammoIcon = LK.getAsset('arrow', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.4,
		scaleY: 1.4,
		rotation: -Math.PI / 2 // Rotate 90 degrees counterclockwise
	});
	// Initialize container
	self.container = new Container();
	self.container.addChild(self.scoreText);
	self.container.addChild(self.coinIcon);
	self.container.addChild(self.ammoText);
	self.container.addChild(self.ammoIcon);
	// 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;
	self.ammoText.x = -150;
	self.ammoText.y = 60; // Place below coin display
	self.ammoIcon.x = 55;
	self.ammoIcon.y = 220; // Place below coin display
	// Add ammo update method
	self.updateAmmo = function (amount) {
		self.ammoText.setText(amount + 'x');
		// Adjust position based on digit count
		if (amount >= 10) {
			self.ammoText.x = -200;
		} else {
			self.ammoText.x = -150;
		}
	};
	// 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;
var scoreContainer = game.addChild(new Container());
// Game state
var gameStarted = false;
var titleScreen;
var playButton;
var gameStartTime;
var tutorialActive = false;
var tutorialManager;
var tutorialButton;
var playButtonFlashInterval;
// Platform management
var platforms = [];
var gameSpeedMultiplier = 1.0;
var platformSpawnCounter = 0;
var platformsUntilNextChange = 0;
var lowPlatformHeight = GAME_HEIGHT / 1.5 + PLAYER_PLATFORM_OFFSET - 100;
var highPlatformHeight = lowPlatformHeight - 600;
var HIGHEST_PLATFORM_HEIGHT = highPlatformHeight - 600;
var LOWEST_PLATFORM_HEIGHT = lowPlatformHeight + 600;
var currentPlatformHeight = lowPlatformHeight;
var lastPlatformHeight = lowPlatformHeight;
var lastPlatformX = 0;
var currentPlatformPattern = 'A'; // 'A' for 1&3, 'B' for 2&4
var currentStraightHeight;
// 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 arrows = [];
var skeletonSwords = [];
var jarSpawnCounter = 0;
var jarSpawnInterval = 10;
var chestSpawnCounter = 0;
var chestSpawnInterval = 75;
var enemies = [];
var enemySpawnInterval = 100;
var goblinSpawnCounter = 0;
var goblinSpawnInterval = 100;
var eyeballSpawnCounter = 0;
var eyeballSpawnInterval = 200;
var skeletonSpawnCounter = 0;
var skeletonSpawnInterval = 350;
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
****/ 
// Screen shake effect function
function applyScreenShake() {
	var shakeIntensity = 30;
	var shakeDuration = 400;
	var shakeCount = 8;
	var originalX = game.x;
	var originalY = game.y;
	// Stop any existing shake
	tween.stop(game, {
		x: true,
		y: true
	});
	// Reset position to ensure we start from 0,0
	game.x = 0;
	game.y = 0;
	// Recursive function to create multiple shakes
	function createShake(count) {
		if (count <= 0) {
			// Final tween to settle back to original position
			tween(game, {
				x: originalX,
				y: originalY
			}, {
				duration: shakeDuration / 4,
				easing: tween.easeOut
			});
			return;
		}
		// Calculate decreasing intensity
		var currentIntensity = shakeIntensity * (count / shakeCount);
		// Random offset direction
		var offsetX = (Math.random() - 0.5) * 2 * currentIntensity;
		var offsetY = (Math.random() - 0.5) * 2 * currentIntensity;
		// Apply shake movement
		tween(game, {
			x: offsetX,
			y: offsetY
		}, {
			duration: shakeDuration / shakeCount,
			easing: tween.linear,
			onFinish: function onFinish() {
				createShake(count - 1);
			}
		});
	}
	// Start the shake sequence
	createShake(shakeCount);
}
// 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,
		alpha: 0
	});
	playButton.x = GAME_WIDTH / 2;
	playButton.y = GAME_HEIGHT / 1.4;
	tween(playButton, {
		alpha: 1
	}, {
		duration: 1000,
		easing: tween.easeIn
	});
	// Add tutorial button
	tutorialButton = titleScreen.attachAsset('tutorialbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0
	});
	tutorialButton.x = GAME_WIDTH / 2;
	tutorialButton.y = GAME_HEIGHT / 1.15;
	tween(tutorialButton, {
		alpha: 1
	}, {
		duration: 1000,
		easing: tween.easeIn
	});
	// Add flashing animation for play button only
	function flashPlayButton() {
		tween(playButton, {
			alpha: 0
		}, {
			duration: 250,
			easing: tween.linear,
			onFinish: function onFinish() {
				tween(playButton, {
					alpha: 1
				}, {
					duration: 250,
					easing: tween.linear
				});
			}
		});
	}
	if (playButtonFlashInterval) {
		LK.clearInterval(playButtonFlashInterval);
	}
	// Flash every 2 seconds
	playButtonFlashInterval = LK.setInterval(flashPlayButton, 2000);
	// Initialize torch decorations
	initializeTorches();
}
// Place torches in the scene
function initializeTorches() {
	// Check if torches already exist
	if (midgroundContainer.children.some(function (child) {
		return child instanceof Torch;
	})) {
		return;
	}
	// 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);
}
function startTutorial() {
	// Clear title screen
	titleScreen.destroy();
	// Initialize game state
	gameStarted = true;
	tutorialActive = true;
	// Create and initialize tutorial manager
	tutorialManager = new TutorialManager();
	tutorialManager.init();
	// Initialize particle system if needed
	if (!particleSystem) {
		particleSystem = new ParticlePool(100);
		game.addChild(particleSystem);
	}
}
// Initialize game elements
function initializeGame() {
	// Create player
	player = game.addChild(new Player());
	player.x = GAME_WIDTH / 4.5;
	player.y = GAME_HEIGHT / 1.5 - 100;
	game.addChild(heartContainer);
	scoreManager.updateAmmo(player.ammoCount);
	// 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;
	currentPlatformPattern = 'A';
	platformsUntilNextChange = 0; // This will trigger new sequence immediately after initial platforms
	player.isOnGround = true;
	player.currentPlatform = platforms[0];
}
// Start the game
function startGame() {
	gameStarted = true;
	gameStartTime = Date.now();
	titleScreen.destroy();
	// Initialize score manager
	scoreManager = new ScoreManager();
	game.addChild(scoreManager.container);
	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 * gameSpeedMultiplier;
	bg2.x -= PLATFORM_SPEED * 0.3 * gameSpeedMultiplier;
	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 * gameSpeedMultiplier;
	mg2.x -= PLATFORM_SPEED * 0.6 * gameSpeedMultiplier;
	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 * gameSpeedMultiplier;
	fg2.x -= PLATFORM_SPEED * gameSpeedMultiplier;
	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 * gameSpeedMultiplier;
			if (child.x <= -GAME_WIDTH) {
				child.x = child.x + GAME_WIDTH * 2;
			}
		}
	}
}
// Update and spawn platforms
function updatePlatforms() {
	var lastPlatform = platforms[platforms.length - 1];
	if (lastPlatform && lastPlatform.x < GAME_WIDTH + 500) {
		// Special case for first platform after initial sequence
		if (!currentPlatformPattern) {
			currentPlatformPattern = 'A';
			platformsUntilNextChange = Math.floor(Math.random() * (MAX_PLATFORMS_IN_SEQUENCE - MIN_PLATFORMS_IN_SEQUENCE + 1)) + MIN_PLATFORMS_IN_SEQUENCE;
		}
		if (platformsUntilNextChange <= 0) {
			// Add chance to switch to pattern C (straight section)
			var patternChance = Math.random();
			if (patternChance < 0.1) {
				currentPlatformPattern = 'C';
				platformsUntilNextChange = Math.floor(Math.random() * 4) + 6;
				currentStraightHeight = Math.random() < 0.5 ? highPlatformHeight : lowPlatformHeight;
			} else {
				currentPlatformPattern = currentPlatformPattern === 'A' ? 'B' : 'A';
				platformsUntilNextChange = Math.floor(Math.random() * (MAX_PLATFORMS_IN_SEQUENCE - MIN_PLATFORMS_IN_SEQUENCE + 1)) + MIN_PLATFORMS_IN_SEQUENCE;
			}
		}
		// Calculate gap probability
		var timeSinceStart = Date.now() - gameStartTime;
		var baseGapChance = 0.025;
		var additionalChance = Math.min(0.10, timeSinceStart / 600000);
		var totalGapChance = baseGapChance + additionalChance;
		if (currentPlatformPattern === 'C') {
			// Single platform for straight sections, using stored height
			var platform = new Platform();
			if (Math.random() < totalGapChance) {
				platform.x = lastPlatform.x + (PLATFORM_WIDTH + 400);
			} else {
				platform.x = lastPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
			}
			platform.y = currentStraightHeight;
			platforms.push(platform);
			game.addChild(platform);
		} else {
			// Regular dual platform patterns with possible double gaps
			var platformUpper = new Platform();
			var platformLower = new Platform();
			// Determine gap type with random chance
			var gapChance = Math.random();
			var hasDoubleGap = gapChance < totalGapChance * 0.3; // 30% of gap chance is double gap
			var hasSingleGap = !hasDoubleGap && gapChance < totalGapChance; // Remaining gap chance is single gap
			// If single gap, randomly choose which platform gets it
			var upperGap = hasDoubleGap || hasSingleGap && Math.random() < 0.5;
			var lowerGap = hasDoubleGap || hasSingleGap && !upperGap;
			// Position upper platform
			if (upperGap) {
				platformUpper.x = lastPlatform.x + (PLATFORM_WIDTH + 400);
			} else {
				platformUpper.x = lastPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
			}
			// Position lower platform
			if (lowerGap) {
				platformLower.x = lastPlatform.x + (PLATFORM_WIDTH + 400);
			} else {
				platformLower.x = lastPlatform.x + (PLATFORM_WIDTH - PLATFORM_OVERLAP);
			}
			if (currentPlatformPattern === 'A') {
				platformUpper.y = HIGHEST_PLATFORM_HEIGHT;
				platformLower.y = lowPlatformHeight;
			} else {
				platformUpper.y = highPlatformHeight;
				platformLower.y = LOWEST_PLATFORM_HEIGHT;
			}
			platforms.push(platformUpper);
			platforms.push(platformLower);
			game.addChild(platformUpper);
			game.addChild(platformLower);
		}
		platformsUntilNextChange--;
	}
	// Update platforms
	for (var i = platforms.length - 1; i >= 0; i--) {
		platforms[i].update();
		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;
		});
		// Try to spawn on multiple platforms
		availablePlatforms.forEach(function (platform) {
			if (Math.random() < 0.25 && GameUtils.canSpawnAtPosition(platform.x)) {
				// Adjusted probability
				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;
		});
		availablePlatforms.forEach(function (platform) {
			if (Math.random() < 0.12 && GameUtils.canSpawnAtPosition(platform.x)) {
				// Adjusted probability
				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();
		var slideAttackBounds = player.getSlideAttackBounds();
		var itemBounds = {
			left: collectible.x - 50,
			right: collectible.x + 50,
			top: collectible.y - 75,
			bottom: collectible.y + 75
		};
		if (attackBounds && GameUtils.checkCollision(attackBounds, itemBounds) || slideAttackBounds && GameUtils.checkCollision(slideAttackBounds, 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() {
	if (!tutorialActive) {
		var isPlatformOccupied = function isPlatformOccupied(platform) {
			return enemies.some(function (enemy) {
				return enemy.currentPlatform === platform && (enemy.type === 'goblin' || enemy.type === 'skeleton');
			});
		}; // Get available platforms for ground enemies
		// Create helper function to check if platform is already taken
		var availablePlatforms = platforms.filter(function (p) {
			return p.x > GAME_WIDTH - 100 && p.x < GAME_WIDTH + 300;
		});
		// Filter out platforms that already have ground enemies
		var unoccupiedPlatforms = availablePlatforms.filter(function (p) {
			return !isPlatformOccupied(p);
		});
		// Goblin spawning
		goblinSpawnCounter++;
		if (goblinSpawnCounter >= goblinSpawnInterval && unoccupiedPlatforms.length > 0) {
			var platform = unoccupiedPlatforms[Math.floor(Math.random() * unoccupiedPlatforms.length)];
			var enemy = new Enemy('goblin');
			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) + 150;
			goblinSpawnCounter = 0;
			// Remove used platform from unoccupied list
			unoccupiedPlatforms = unoccupiedPlatforms.filter(function (p) {
				return p !== platform;
			});
		}
		// Skeleton spawning - only try if there are still unoccupied platforms
		skeletonSpawnCounter++;
		if (skeletonSpawnCounter >= skeletonSpawnInterval && unoccupiedPlatforms.length > 0) {
			var platform = unoccupiedPlatforms[Math.floor(Math.random() * unoccupiedPlatforms.length)];
			var enemy = new Enemy('skeleton');
			enemy.x = platform.x;
			enemy.y = platform.y - ENEMY_PLATFORM_OFFSET;
			enemy.currentPlatform = platform;
			enemies.push(enemy);
			game.addChild(enemy);
			skeletonSpawnInterval = Math.floor(Math.random() * 200) + 350;
			skeletonSpawnCounter = 0;
		}
		// Eyeball spawning - updated height range
		eyeballSpawnCounter++;
		if (eyeballSpawnCounter >= eyeballSpawnInterval) {
			var enemy = new Enemy('eyeball');
			var heightRange = LOWEST_PLATFORM_HEIGHT - HIGHEST_PLATFORM_HEIGHT;
			var randomHeight = Math.random() * heightRange;
			enemy.x = GAME_WIDTH + 100;
			enemy.y = HIGHEST_PLATFORM_HEIGHT + randomHeight; // Spawn anywhere between highest and lowest
			enemies.push(enemy);
			game.addChild(enemy);
			eyeballSpawnInterval = Math.floor(Math.random() * 300) + 250;
			eyeballSpawnCounter = 0;
		}
	}
	// Update enemies and check collisions
	for (var j = enemies.length - 1; j >= 0; j--) {
		enemies[j].update();
		// Check if enemy has been destroyed
		if (enemies[j].destroyed) {
			enemies.splice(j, 1);
			continue;
		}
		// Skip if enemy is far behind player
		if (enemies[j].x < player.x - 100) {
			continue;
		}
		var playerBounds = player.getCollisionBounds();
		var enemyBounds = enemies[j].getBounds();
		var attackBounds = player.getAttackBounds();
		var slideAttackBounds = player.getSlideAttackBounds();
		// 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 if (enemies[j].type === 'skeleton') {
					LK.getSound('skeletonhit').play();
				} else {
					LK.getSound('enemyhit').play();
				}
				continue;
			}
		}
		// In the updateEnemies() function, inside the enemy update loop
		for (var i = arrows.length - 1; i >= 0; i--) {
			var arrow = arrows[i];
			if (GameUtils.checkCollision(arrow.getBounds(), enemyBounds)) {
				// Only hit the enemy and destroy arrow if enemy is not already hit or dying
				if (!enemies[j].isHit && !enemies[j].isDying) {
					enemies[j].hit();
					if (enemies[j].type === 'eyeball') {
						LK.getSound('eyeballhit').play();
					} else if (enemies[j].type === 'skeleton') {
						LK.getSound('skeletonhit').play();
					} else {
						LK.getSound('enemyhit').play();
					}
					// Only destroy the arrow if it actually damages an enemy
					arrow.destroy();
					arrows.splice(i, 1);
					break;
				}
				// Arrows pass through already hit/dying enemies
			}
		}
		for (var i = skeletonSwords.length - 1; i >= 0; i--) {
			if (!skeletonSwords[i]) {
				skeletonSwords.splice(i, 1);
				continue;
			}
			skeletonSwords[i].update();
			// Check collision with player
			if (GameUtils.checkCollision(skeletonSwords[i].getBounds(), playerBounds)) {
				if (!player.isInvulnerable && !player.isDying) {
					player.takeDamage();
					skeletonSwords[i].destroy();
					skeletonSwords.splice(i, 1);
				}
			}
			// Remove destroyed swords - safely check if the sword exists first and if it has destroyed property
			if (skeletonSwords[i] && skeletonSwords[i].destroyed === true) {
				skeletonSwords.splice(i, 1);
			}
		}
		// Check for slide collision
		if (slideAttackBounds && !enemies[j].isHit && !enemies[j].isDying) {
			if (enemies[j].x > player.x && GameUtils.checkCollision(slideAttackBounds, enemyBounds)) {
				enemies[j].slideHit();
				continue;
			}
		}
		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
// Modify the main game update function
game.update = function () {
	// Always update backgrounds
	updateBackgrounds();
	if (!gameStarted) {
		return;
	}
	// Update player and particles
	player.update();
	if (particleSystem) {
		particleSystem.update();
	}
	// Update arrows
	for (var i = arrows.length - 1; i >= 0; i--) {
		arrows[i].update();
		if (arrows[i].destroyed) {
			arrows.splice(i, 1);
		}
	}
	if (tutorialActive) {
		tutorialManager.update();
	} else {
		// Normal game updates
		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 playButtonBounds = {
			left: playButton.x - playButton.width / 2,
			right: playButton.x + playButton.width / 2,
			top: playButton.y - playButton.height / 2,
			bottom: playButton.y + playButton.height / 2
		};
		var tutorialButtonBounds = {
			left: tutorialButton.x - tutorialButton.width / 2,
			right: tutorialButton.x + tutorialButton.width / 2,
			top: tutorialButton.y - tutorialButton.height / 2,
			bottom: tutorialButton.y + tutorialButton.height / 2
		};
		if (x >= playButtonBounds.left && x <= playButtonBounds.right && y >= playButtonBounds.top && y <= playButtonBounds.bottom) {
			startGame();
		} else if (x >= tutorialButtonBounds.left && x <= tutorialButtonBounds.right && y >= tutorialButtonBounds.top && y <= tutorialButtonBounds.bottom) {
			startTutorial();
		}
		return;
	}
	// Prevent input if player is dying or air dying
	if (player && (player.isDying || player.dyingInAir)) {
		return;
	}
	touchEndX = x;
	touchEndY = y;
	if (tutorialActive) {
		tutorialManager.checkInput(touchStartX, touchStartY, touchEndX, touchEndY);
	}
	var deltaY = touchEndY - touchStartY;
	var deltaX = touchEndX - touchStartX;
	// Ignore very small movements
	if (Math.abs(deltaY) < VERTICAL_DEADZONE && Math.abs(deltaX) < VERTICAL_DEADZONE) {
		player.attack(); // Just treat as tap/attack
		return;
	}
	if (Math.abs(deltaX) > Math.abs(deltaY)) {
		// Horizontal swipe
		if (deltaX > 60) {
			// Right swipe - trigger slide
			player.slide();
		} else if (deltaX < -60) {
			// Left swipe - trigger bow shot
			player.shoot();
		}
	} else if (deltaY > 120) {
		// Downward swipe
		player.fallThrough();
	} else if (deltaY < -120) {
		// Upward swipe - existing jump code
		var currentTime = Date.now();
		if (currentTime - lastJumpTime > JUMP_COOLDOWN) {
			player.jump();
			lastJumpTime = currentTime;
		}
	} else {
		// Tap - existing attack code
		player.attack();
	}
};
game.move = function (x, y, obj) {
	if (!gameStarted) {
		return;
	}
	var deltaX = x - touchStartX;
	if (Math.abs(deltaX) > Math.abs(y - touchStartY)) {
		if (deltaX > SLIDE_MOVE_THRESHOLD) {
			player.slide();
		} else if (deltaX < -SLIDE_MOVE_THRESHOLD) {
			player.shoot();
		}
	}
	// Check for downward swipe
	var deltaY = y - touchStartY;
	if (deltaY > SLIDE_MOVE_THRESHOLD) {
		player.fallThrough();
	}
	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
@@ -3647,12 +3647,12 @@
 		return;
 	}
 	if (Math.abs(deltaX) > Math.abs(deltaY)) {
 		// Horizontal swipe
-		if (deltaX > 70) {
+		if (deltaX > 60) {
 			// Right swipe - trigger slide
 			player.slide();
-		} else if (deltaX < -70) {
+		} else if (deltaX < -60) {
 			// Left swipe - trigger bow shot
 			player.shoot();
 		}
 	} else if (deltaY > 120) {
:quality(85)/https://cdn.frvr.ai/67589518e1c6df8afc3f7fe5.png%3F3) 
 2D Single Monster. In-Game asset. 2d. Blank background. High contrast. No shadows..
:quality(85)/https://cdn.frvr.ai/67dffdd6e7971efb59b9487b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f46e0d8224f7767099b16a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f486b349870633be8d76a4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f486ea49870633be8d76a6.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4870e49870633be8d76a8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4873349870633be8d76aa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4875349870633be8d76ac.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4954749870633be8d76b0.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4957149870633be8d76b2.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f4959d49870633be8d76b4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57c13e077678de9f553a9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57c4ddba95c5c44cbfe77.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57c7ddba95c5c44cbfe79.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57c9cdba95c5c44cbfe7b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57cc3dba95c5c44cbfe7d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57ce2dba95c5c44cbfe7f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57d08dba95c5c44cbfe81.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f57d31dba95c5c44cbfe83.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5af6ae077678de9f553b3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5af9ce077678de9f553b5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5afc6dba95c5c44cbfe89.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5aff3e077678de9f553b7.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5b013e077678de9f553b9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5ea42e077678de9f553ce.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5ea75e077678de9f553d0.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5eaaddba95c5c44cbfebb.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5eacbdba95c5c44cbfebd.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f5eafcdba95c5c44cbfebf.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7440203de48563abd51c8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7451503de48563abd51d4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7453b03de48563abd51da.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7f7d1dbbedb25886c4a1a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7f831dbbedb25886c4a2f.png%3F3) 
 A gold coin. 8 bit pixel art. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f835da55d0d3e7a7c690bc.png%3F3) 
 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
:quality(85)/https://cdn.frvr.ai/67f83ca958b4222a3fa33415.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f88cd87dff3a1a41deeef4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f88d107dff3a1a41deeefa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91cef58b4222a3fa33468.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91d0e8950217c2b74b7cc.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91d378950217c2b74b7d2.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91d528950217c2b74b7d4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91d748950217c2b74b7d6.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91d958950217c2b74b7d8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91db38950217c2b74b7da.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f91dd08950217c2b74b7dc.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f954178950217c2b74b7fa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f954968950217c2b74b7fe.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f954be58b4222a3fa33472.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f954e78950217c2b74b800.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f98ebad04d00a3193c4fdc.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f98fded04d00a3193c5018.png%3F3) 
 A ruby. Pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f99040d04d00a3193c502b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f99180d04d00a3193c507c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f998acb0ec885d683370cb.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f998ebb0ec885d683370d1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f99913b0ec885d683370d7.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f99b4a3402bac7da9ae8fa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f9ba2601e5b663933736a9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f9d60f58b4222a3fa33484.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fc05e951706adb0685a2c1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fc2d9d78ae65f2df3d4b19.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fdca748950217c2b74b952.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fdcaa68950217c2b74b954.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fdcaf358b4222a3fa33623.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fdcb198950217c2b74b956.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67fdcb3e8950217c2b74b958.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff23f958b4222a3fa3368a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff241b58b4222a3fa3368c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff245b58b4222a3fa3368e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff24a358b4222a3fa33692.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff24c858b4222a3fa33694.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff24fb58b4222a3fa33696.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff251f58b4222a3fa33698.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff26c08950217c2b74b9c1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff26e38950217c2b74b9c3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff27078950217c2b74b9c5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67ff272d8950217c2b74b9c7.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6801510f0ae5b5511299f9d8.png%3F3) 
 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
:quality(85)/https://cdn.frvr.ai/6801516e790eec70f999c0df.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680151c7790eec70f999c0e1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680151f0b2eb078281a44941.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68015217b2eb078281a44943.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68015329790eec70f999c0e3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68015356790eec70f999c0e5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6801537ab2eb078281a4494b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680153a8b2eb078281a4494d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680153d2b2eb078281a4494f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680176360ae5b5511299fb18.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804009624056734469b98e9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680400c924056734469b98eb.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680400ec24056734469b98ed.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804010724056734469b98ef.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804052bd7f48af3888d9443.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804054fd7f48af3888d9445.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68040593d7f48af3888d9447.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680405c3d7f48af3888d9449.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680405e2d7f48af3888d944b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68040601d7f48af3888d944d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804066ad7f48af3888d944f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6804068bd7f48af3888d9451.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680406b0d7f48af3888d9453.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680406d9d7f48af3888d9455.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68040703d7f48af3888d9457.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68040722d7f48af3888d9459.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680408893f67cda9b9aa7c5e.png%3F3) 
 A thin crude sword, no pommel. 8 bit pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/6805760fa81ddab354aa4644.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6805764969da123ac6891500.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6805766ca81ddab354aa4646.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68057699a81ddab354aa4648.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806dc266ccbe336fdb10662.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806dc896ccbe336fdb10668.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806dd0b6ccbe336fdb10674.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806dd6e6ccbe336fdb10683.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806ddca6ccbe336fdb10689.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806de1a6ccbe336fdb10691.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806de686ccbe336fdb10697.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6806ded96ccbe336fdb1069d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68070d05d1ce2d138266d6c9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6807297cd1ce2d138266d742.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680729d6d1ce2d138266d748.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68072a1cd1ce2d138266d74e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68072a51d1ce2d138266d756.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68096292c8e430ed8ad2c646.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680962e4c8e430ed8ad2c64c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6809633bc8e430ed8ad2c652.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68096363c8e430ed8ad2c658.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6809870806312177649e9ca3.png%3F3) 
 An icon of white wings. Pixel art.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/680987cb06312177649e9cac.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a90014cffebef77d59363.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a90634cffebef77d5936a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a90c94cffebef77d59371.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a91144cffebef77d5937a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a91544cffebef77d59380.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a919e4cffebef77d59386.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a91f14cffebef77d59390.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a926d4cffebef77d59399.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a92bb4cffebef77d593a5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a930e4cffebef77d593ab.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680a935a4cffebef77d593b1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa10a33dfe52b720e77fe.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa15633dfe52b720e780a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa1b733dfe52b720e7818.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa1fb33dfe52b720e7823.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa24f33dfe52b720e783c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa29833dfe52b720e7845.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa2db33dfe52b720e784c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa31433dfe52b720e7853.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa34c33dfe52b720e785d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa38e33dfe52b720e7866.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa4428f7db8d46df4fe8f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa4888f7db8d46df4fe96.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa78933dfe52b720e78a1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa7e333dfe52b720e78aa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa81d33dfe52b720e78b3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa86933dfe52b720e78b9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa8b033dfe52b720e78bf.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aa8f633dfe52b720e78c5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aac91a54bca03c1d3dcfb.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aacd3a54bca03c1d3dd01.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aad07a54bca03c1d3dd0b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aad3aa54bca03c1d3dd11.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aad91a54bca03c1d3dd17.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aaddda54bca03c1d3dd1d.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aae1ba54bca03c1d3dd30.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aaeeca54bca03c1d3dd39.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aaf2ca54bca03c1d3dd3f.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aaf9ca54bca03c1d3dd45.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680aaff8a54bca03c1d3dd4b.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680ab05aa54bca03c1d3dd51.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680ab0baa54bca03c1d3dd57.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680bc1d0b5e28bcfaf60fdba.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680bc201b5e28bcfaf60fdc3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680bc234b5e28bcfaf60fdc9.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/680c0840ec35c168ea3e21e8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/681d7acace5c2130acf29121.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682268c9ce5c2130acf29261.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682268f1ce5c2130acf29263.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68226973ce5c2130acf29265.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682269adce5c2130acf29267.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682269dece5c2130acf29269.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68241e2070c71d8855efef52.png%3F3) 
 a magnet icon. 8 bit pixel art. In-Game asset. 2d. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/68263754c3fcd10c97dcdf11.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68263776eae88ff12519aab3.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68263797c3fcd10c97dcdf13.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682637fbc3fcd10c97dcdf15.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826381ee5bdf92eeae9b3f8.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68263854e5bdf92eeae9b3fa.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682638e2d1f9b2dbab30ce45.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68263942e5bdf92eeae9b3fc.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682666b6e5bdf92eeae9b418.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826670ce5bdf92eeae9b41a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826673de5bdf92eeae9b41c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68266762e5bdf92eeae9b41e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68266789e5bdf92eeae9b420.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682667ace5bdf92eeae9b422.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826816fe5bdf92eeae9b42a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682681d6e5bdf92eeae9b42c.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682681f9e5bdf92eeae9b42e.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/68268221e5bdf92eeae9b430.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826825be5bdf92eeae9b432.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682682cce5bdf92eeae9b434.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/682682ffe5bdf92eeae9b436.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826834fe5bdf92eeae9b438.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6826837ae5bdf92eeae9b43a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/683617277971cde6cac52093.png%3F3) 
 An icon of a glowing wooden arrow from a bow trailing pink particles. Pixel art.. In-Game asset. 2d. 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
skeletonhit
Sound effect
gameover2
Sound effect
shopbuy
Sound effect
menuselect
Sound effect
cantbuy
Sound effect
startgame
Sound effect
platformcrumble
Sound effect
rocksfall
Sound effect
airdash
Sound effect
groundimpact
Sound effect
groundsmashfalling
Sound effect
slide
Sound effect
mushroomburst
Sound effect
mushroomhiss
Sound effect
mushroomdie
Sound effect
mushroombounce
Sound effect