/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.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; // Magnitude of velocity
	self.damage = 1;
	self.startX = 0; // Initial X position, set by Player
	self.startY = 0; // Initial Y position, set by Player
	self.rotation = 0; // Current angle of arrow in radians
	self.velocityX = 0; // X component of velocity
	self.velocityY = 0; // Y component of velocity
	var MAX_DISTANCE_FOR_FULL_HOMING = 400; // Pixels after which homing is at max strength (Reduced from 800)
	var MAX_TURN_RATE = 0.018; // Max radians per frame the arrow can turn (Further reduced from 0.035)
	self.update = function () {
		if (UPGRADE_REGISTRY.tracking_arrow > 0 && enemies && enemies.length > 0 && player) {
			// Added player check
			var closestEnemy = null;
			var minDistanceSq = Infinity;
			for (var i = 0; i < enemies.length; i++) {
				var enemy = enemies[i];
				// Condition to ensure enemy is in front of the player
				if (!enemy || enemy.isDying || enemy.isHit || enemy.destroyed || enemy.x < player.x) {
					continue;
				}
				var dx = enemy.x - self.x;
				var dy = enemy.y - self.y;
				var distSq = dx * dx + dy * dy;
				if (distSq < minDistanceSq) {
					minDistanceSq = distSq;
					closestEnemy = enemy;
				}
			}
			if (closestEnemy) {
				// No need to check closestEnemy.x < player.x again, already handled in loop
				var traveledDx = self.x - self.startX;
				var traveledDy = self.y - self.startY;
				var distanceTraveled = Math.sqrt(traveledDx * traveledDx + traveledDy * traveledDy);
				var homingFactor = 0;
				if (MAX_DISTANCE_FOR_FULL_HOMING > 0) {
					homingFactor = Math.min(1.0, distanceTraveled / MAX_DISTANCE_FOR_FULL_HOMING);
				}
				var targetAngle = Math.atan2(closestEnemy.y - self.y, closestEnemy.x - self.x);
				var angleDiff = targetAngle - self.rotation;
				while (angleDiff > Math.PI) {
					angleDiff -= 2 * Math.PI;
				}
				while (angleDiff < -Math.PI) {
					angleDiff += 2 * Math.PI;
				}
				var turnAmount = Math.max(-MAX_TURN_RATE, Math.min(MAX_TURN_RATE, angleDiff)) * homingFactor;
				self.rotation += turnAmount;
			}
		}
		self.velocityX = Math.cos(self.rotation) * self.speed;
		self.velocityY = Math.sin(self.rotation) * self.speed;
		self.x += self.velocityX * gameSpeedMultiplier;
		self.y += self.velocityY * gameSpeedMultiplier;
		self.sprite.rotation = self.rotation;
		// Add particle effect for tracking arrows
		if (UPGRADE_REGISTRY.tracking_arrow > 0 && LK.ticks % 3 === 0) {
			// Spawn every 3 ticks for a denser trail
			// Calculate spawn position slightly behind the arrow's tail
			var arrowTailOffsetX = Math.cos(self.rotation) * (self.sprite.width * 0.5 + 10);
			var arrowTailOffsetY = Math.sin(self.rotation) * (self.sprite.width * 0.5 + 10);
			var particleX = self.x - arrowTailOffsetX;
			var particleY = self.y - arrowTailOffsetY;
			var particle = new ArrowParticle(particleX, particleY);
			game.addChild(particle);
			arrowParticles.push(particle);
		}
		// Off-screen destruction logic is now handled in game.update
	};
	self.getBounds = function () {
		// A slightly more generous bounding box to account for rotation.
		// Uses the sprite's largest dimension to define a square box.
		var arrowWidth = self.sprite.width > 0 ? self.sprite.width : 120;
		var arrowHeight = self.sprite.height > 0 ? self.sprite.height : 31.2;
		var size = Math.max(arrowWidth, arrowHeight) * 0.6; // Make it a bit smaller than full diagonal
		return {
			left: self.x - size / 2,
			right: self.x + size / 2,
			top: self.y - size / 2,
			bottom: self.y + size / 2
		};
	};
	self.updateAmmo = function (amount) {
		// This method seems to belong to a UI element, not the Arrow projectile itself.
		// Keeping it here as per original structure, but it might be an error in original code.
		// Assuming self.ammoText exists if this method is called.
		if (self.ammoText) {
			self.ammoText.setText('x' + amount);
			if (amount >= 10) {
				self.ammoText.x = -150;
			} else {
				self.ammoText.x = -100;
			}
		}
	};
	return self;
});
var ArrowParticle = Container.expand(function (x, y) {
	var self = Container.call(this);
	self.sprite = self.attachAsset('pixel', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.2,
		// Increased particle size
		// Small particles
		scaleY: 0.2,
		// Increased particle size
		alpha: 0.9,
		// Initial alpha
		tint: 0xFF69B4 // Pink color for particles
	});
	self.x = x;
	self.y = y;
	self.driftX = (Math.random() - 0.5) * 1.5; // Random horizontal drift
	self.driftY = (Math.random() - 0.5) * 1.5 + 0.75; // Add a consistent downward push, ensuring particles mostly drift down
	self.destroyQueued = false;
	var durationMs = 200 + Math.random() * 200; // Lifespan of 200-400ms
	tween(self.sprite, {
		alpha: 0
	}, {
		duration: durationMs,
		easing: tween.linear,
		// Simple linear fade
		onFinish: function onFinish() {
			self.destroyQueued = true;
		}
	});
	self.update = function () {
		if (self.destroyQueued) {
			// If marked for destruction by tween, do nothing further here.
			// game.update will handle actual removal.
			return;
		}
		self.x += self.driftX;
		self.y += self.driftY;
		// Particles also need to scroll with the screen
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
	};
	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;
});
// CrumblePiece class
var CrumblePiece = Container.expand(function (pieceNum) {
	var self = Container.call(this);
	self.sprite = self.attachAsset('crumblepiece' + pieceNum, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.rotationSpeed = 0;
	self.update = function () {
		// Apply physics
		self.velocityY += 0.5; // gravity
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.rotation += self.rotationSpeed;
		// Destroy if off screen
		if (self.y > GAME_HEIGHT || self.x < -100) {
			self.destroy();
		}
	};
	return self;
});
var CustomText = Container.expand(function (text, options) {
	var self = Container.call(this);
	// Basic settings
	self.size = options.size || 70;
	self.scaleSize = self.size / 70;
	self.letterSpacing = (options.letterSpacing || 10) * self.scaleSize;
	self.fill = options.fill || 0xFFFFFF;
	self.wordWrap = options.wordWrap || false;
	self.wordWrapWidth = options.wordWrapWidth || 800;
	self.lineHeight = (options.lineHeight || 1.2) * self.size;
	self.letters = [];
	self.letterPool = {}; // Pool of letter sprites by character
	self.text = '';
	self.width = 0;
	self.height = 0; // Track height for multi-line text
	// Get asset name for character - keep original mapping logic
	self.getAssetForChar = function (_char) {
		if (_char.match(/[a-z]/i)) {
			return 'letter_' + _char.toLowerCase();
		} else if (_char.match(/[0-9]/)) {
			return 'number_' + _char;
		} else {
			switch (_char) {
				case '.':
					return 'period_mark';
				case ',':
					return 'comma_mark';
				case '!':
					return 'exclamation_mark';
				case '?':
					return 'question_mark';
				case '+':
					return 'addition_mark';
				case '*':
					return 'multiplication_mark';
				case ' ':
					return null;
				default:
					return null;
			}
		}
	};
	// Get or create sprite from pool
	self.getOrCreateSprite = function (_char2) {
		// Initialize pool for this character if needed
		if (!self.letterPool[_char2]) {
			self.letterPool[_char2] = [];
		}
		// Try to find an inactive sprite
		var sprite = self.letterPool[_char2].find(function (s) {
			return !s.active;
		});
		// Create new sprite if none available
		if (!sprite) {
			var assetName = self.getAssetForChar(_char2);
			if (assetName) {
				sprite = self.attachAsset(assetName, {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: self.scaleSize,
					scaleY: self.scaleSize
				});
				if (self.fill) {
					sprite.tint = self.fill;
				}
				sprite.active = false;
				sprite.alpha = 0;
				// Store character type for position adjustments
				sprite.charType = _char2;
				self.letterPool[_char2].push(sprite);
			}
		}
		return sprite;
	};
	// Calculate the width a text would take
	self.calculateTextWidth = function (text) {
		var width = 0;
		for (var i = 0; i < text.length; i++) {
			var _char4 = text[i];
			if (_char4 === ' ') {
				width += 40 * self.scaleSize;
			} else {
				width += 50 * self.scaleSize + self.letterSpacing;
			}
		}
		return width;
	};
	// Break text into lines for word wrap
	self.breakTextIntoLines = function (text) {
		// First handle explicit line breaks
		if (text.includes('\n')) {
			var explicitLines = text.split('\n');
			var result = [];
			// Process each explicit line for word wrapping
			for (var j = 0; j < explicitLines.length; j++) {
				if (self.wordWrap) {
					var wrappedLines = this.breakTextIntoLines(explicitLines[j]);
					result = result.concat(wrappedLines);
				} else {
					result.push(explicitLines[j]);
				}
			}
			return result;
		}
		if (!self.wordWrap) {
			return [text];
		}
		var words = text.split(' ');
		var lines = [];
		var currentLine = '';
		var currentWidth = 0;
		var spaceWidth = 40 * self.scaleSize;
		for (var i = 0; i < words.length; i++) {
			var word = words[i];
			var wordWidth = self.calculateTextWidth(word);
			// Check if adding this word would exceed the line width
			if (currentWidth + wordWidth > self.wordWrapWidth && currentLine !== '') {
				lines.push(currentLine.trim());
				currentLine = word + ' ';
				currentWidth = wordWidth + spaceWidth;
			} else {
				currentLine += word + ' ';
				currentWidth += wordWidth + spaceWidth;
			}
		}
		// Add the last line if there's anything left
		if (currentLine.trim() !== '') {
			lines.push(currentLine.trim());
		}
		return lines;
	};
	// Set text content with sprite pooling
	self.setText = function (newText) {
		// Always convert to string
		newText = String(newText);
		// Deactivate all current letters
		self.letters.forEach(function (letter) {
			letter.active = false;
			letter.alpha = 0;
		});
		self.letters = [];
		// Break text into lines if word wrap is enabled
		var lines = self.breakTextIntoLines(newText);
		var maxLineWidth = 0;
		// Position tracking
		var currentX = 0;
		var currentY = 0;
		var lineCount = 0;
		// Process each line
		for (var lineIndex = 0; lineIndex < lines.length; lineIndex++) {
			var line = lines[lineIndex];
			currentX = 0; // Reset X position for each line
			// Create/reuse sprites for each character in this line
			for (var i = 0; i < line.length; i++) {
				var _char3 = line[i];
				// Handle spaces
				if (_char3 === ' ') {
					currentX += 40 * self.scaleSize;
					continue;
				}
				// Get or create sprite
				var sprite = self.getOrCreateSprite(_char3);
				if (sprite) {
					sprite.active = true;
					sprite.alpha = 1;
					sprite.x = currentX;
					sprite.y = currentY;
					// Add vertical position adjustment for punctuation
					if (_char3 === '.' || _char3 === ',') {
						sprite.y += 20 * self.scaleSize; // Move periods and commas down
					} else if (_char3 === '*') {
						sprite.y += 10 * self.scaleSize; // Move multiplication mark down slightly
					}
					self.letters.push(sprite);
					currentX += 50 * self.scaleSize + self.letterSpacing;
				}
			}
			// Track the maximum line width
			if (currentX > maxLineWidth) {
				maxLineWidth = currentX;
			}
			// Move to next line
			if (lineIndex < lines.length - 1) {
				currentY += self.lineHeight;
				lineCount++;
			}
		}
		// Update width and height properties
		self.width = maxLineWidth;
		self.height = (lineCount + 1) * self.lineHeight;
		if (self.letters.length === 0) {
			self.width = 0;
			self.height = 0;
		}
		// Handle anchor alignment
		if (options.anchorX === 1) {
			// Right-aligned text
			for (var i = 0; i < self.letters.length; i++) {
				self.letters[i].x -= self.width;
			}
		} else if (options.anchorX === 0.5) {
			// Center-aligned each line
			var currentLine = 0;
			var lineStart = 0;
			var lineEnd = 0;
			var lineWidth = 0;
			for (var i = 0; i < self.letters.length; i++) {
				// Check if we're in a new line
				var letterY = self.letters[i].y;
				if (i === 0 || letterY !== self.letters[lineStart].y) {
					// Process previous line if this isn't the first letter
					if (i > 0) {
						lineEnd = i - 1;
						lineWidth = self.letters[lineEnd].x + 50 * self.scaleSize / 2;
						// Center this line
						for (var j = lineStart; j <= lineEnd; j++) {
							self.letters[j].x -= lineWidth / 2;
						}
					}
					// Start tracking new line
					lineStart = i;
				}
			}
			// Process the last line
			if (lineStart < self.letters.length) {
				lineEnd = self.letters.length - 1;
				lineWidth = self.letters[lineEnd].x + 50 * self.scaleSize / 2;
				// Center this line
				for (var j = lineStart; j <= lineEnd; j++) {
					self.letters[j].x -= lineWidth / 2;
				}
			}
		}
		// Store current text
		self.text = newText;
	};
	// Cleanup method
	self.destroy = function () {
		// Properly destroy all pooled sprites
		Object.values(self.letterPool).forEach(function (pool) {
			pool.forEach(function (sprite) {
				if (sprite.destroy) {
					sprite.destroy();
				}
			});
		});
		self.letterPool = {};
		self.letters = [];
		Container.prototype.destroy.call(this);
	};
	// Initialize with provided text
	self.setText(text);
	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;
	// Add mushroom-specific properties
	self.isAttacking = false;
	self.attackTimer = 0;
	self.attackDuration = 100; // 60 frames for attack animation
	self.attackCooldown = 180; // Cooldown before next attack
	self.attackCooldownTimer = 0;
	self.sporesReleased = false; // Flag to ensure spores only released once per attack
	// 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.11;
	// Define all animation arrays based on enemy type
	if (self.type === 'mushroom') {
		// Mushroom-specific properties
		self.runFrame = 0;
		self.speed = 6;
		self.runAnimation = ['mushroomrun1', 'mushroomrun2', 'mushroomrun3', 'mushroomrun4', 'mushroomrun5', 'mushroomrun6', 'mushroomrun7', 'mushroomrun8'];
		self.hitAnimation = ['mushroomhit1', 'mushroomhit2']; // New hit animations
		self.dieAnimation = ['mushroomdie1', 'mushroomdie2', 'mushroomdie3', 'mushroomdie4']; // New death animations
	} else 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);
		}
	};
	self.initMushroomSprites = 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);
		}
		for (var i = 0; i < 5; i++) {
			var sprite = self.attachAsset('mushroomattack' + (i + 1), {
				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.75;
			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) && self.throwingTimer > Math.floor(self.throwingDuration * 0.8) - 2 && !self.swordThrown) {
			// Flag to ensure we only throw once during this window
			self.swordThrown = true;
			// Create and initialize sword with more robust positioning
			var sword = new SkeletonSword();
			sword.x = self.x - 100; // Fixed position relative to skeleton
			sword.y = self.y - 100;
			// Explicitly set initial velocity independent of gameSpeedMultiplier
			sword.constantVelocity = 8; // Fixed velocity
			// Add the sword to the game and tracking array
			game.addChild(sword);
			skeletonSwords.push(sword);
			// Show throwing frame
			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.swordThrown = false; // Reset the flag
			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.swordThrown = false; // Initialize flag here
				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;
	};
	self.updateMushroomNormalState = function () {
		// Check if should attack
		if (self.checkMushroomAttack()) {
			return; // Skip normal update if starting attack
		}
		// Decrease attack cooldown timer
		if (self.attackCooldownTimer > 0) {
			self.attackCooldownTimer--;
		}
		// Keep existing normal state code
		// 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 mushroom 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
		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.updateMushroomHitState = function () {
		// Same movement physics as before
		self.x += self.throwBackSpeed;
		self.throwBackDistance += Math.abs(self.throwBackSpeed);
		self.throwBackSpeed *= 0.95;
		// Platform collision check (same as before)
		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 && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
						self.currentPlatform = otherPlatform;
						foundAnotherPlatform = true;
						break;
					}
				}
				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.75;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// Show hit animation - Sequential progression through hit frames
		var hitOffset = self.runAnimation.length;
		// Calculate hit frame based on timer
		// If we have 2 hit frames and hitTimer starts at 35, show first frame for first half, second frame for second half
		var hitProgress = 1 - self.hitTimer / 35; // Normalize from 0 to 1
		var hitFrame = Math.min(Math.floor(hitProgress * self.hitAnimation.length), self.hitAnimation.length - 1);
		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';
		}
	};
	self.updateMushroomDyingState = function () {
		// Same movement physics as before
		self.x += self.throwBackSpeed;
		// Platform collision check (same as before)
		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 && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
						self.currentPlatform = otherPlatform;
						foundAnotherPlatform = true;
						break;
					}
				}
				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 - Updated to progress through all frames
		var dieOffset = self.runAnimation.length + self.hitAnimation.length;
		// Progress frame every 15 frames (similar to other enemy death animations)
		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();
			}
		}
	};
	self.checkMushroomAttack = function () {
		if (self.type !== 'mushroom' || self.isHit || self.isDying || self.isAttacking || self.attackCooldownTimer > 0) {
			return false;
		}
		// Attack when player is in range (horizontal and vertical distance check)
		var distanceX = Math.abs(player.x - self.x);
		var distanceY = Math.abs(player.y - self.y);
		if (distanceX < 800 && distanceY < 700 && self.x > player.x) {
			// Store original position relative to platform BEFORE changing state
			self.platformOffset = self.currentPlatform ? self.x - self.currentPlatform.x : 0;
			// Set attacking state
			self.isAttacking = true;
			self.attackTimer = self.attackDuration;
			self.sporesReleased = false;
			self.originalSpeed = self.speed;
			// Don't change position here - we'll maintain platform-relative position
			return true;
		}
		return false;
	};
	// Add mushroom attack animation update
	self.updateMushroomAttackState = function () {
		// Get offset to find attack animation sprites
		var attackOffset = self.runAnimation.length + self.hitAnimation.length + self.dieAnimation.length;
		// Calculate which frame to display based on attack progress
		var frameProgress = (self.attackDuration - self.attackTimer) / self.attackDuration;
		var attackFrame = Math.min(Math.floor(frameProgress * 5), 4);
		// Show current attack frame
		self.sprites[attackOffset + attackFrame].alpha = 1;
		// Play hiss sound when attack animation starts
		if (attackFrame === 0 && self.attackTimer === self.attackDuration - 1) {
			LK.getSound('mushroomhiss').play();
		}
		// Release spores at frame 3 (middle of animation)
		if (attackFrame === 3 && !self.sporesReleased) {
			self.releaseSpores();
			self.sporesReleased = true;
		}
		// If on a platform, maintain position relative to that platform
		if (self.currentPlatform) {
			// Use stored platform offset (which is 0 if not previously set)
			self.x = self.currentPlatform.x + (self.platformOffset || 0);
		} else {
			// If not on a platform, just match platform movement speed
			self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		}
		// Count down attack timer
		self.attackTimer--;
		// End attack when timer expires
		if (self.attackTimer <= 0) {
			self.isAttacking = false;
			self.speed = self.originalSpeed || 6;
			self.attackCooldownTimer = self.attackCooldown;
		}
	};
	// Function to release spores in the specified pattern
	self.releaseSpores = function () {
		// Create 5 spores in pattern described
		var directions = [{
			x: 1,
			y: 0
		},
		// Right
		{
			x: -1,
			y: 0
		},
		// Left
		{
			x: 0,
			y: -1
		},
		// Up
		{
			x: 0.7,
			y: -0.7
		},
		// Up-right diagonal
		{
			x: -0.7,
			y: -0.7
		} // Up-left diagonal
		];
		// Calculate an offset position IN FRONT of the mushroom
		// This compensates for the platform movement and animation delay
		var offsetX = self.x; // Add a significant forward offset
		var offsetY = self.y - 50; // Slightly above as before
		// Play mushroom burst sound effect
		LK.getSound('mushroomburst').play();
		for (var i = 0; i < directions.length; i++) {
			var spore = new Spore();
			// Position spores at the offset position
			spore.x = offsetX;
			spore.y = offsetY;
			// Don't add PLATFORM_SPEED to the velocityX, as that's causing over-compensation
			spore.velocityX = directions[i].x * spore.speed;
			spore.velocityY = directions[i].y * spore.speed;
			// Add to game
			game.addChild(spore);
			// Add to a global array for tracking if needed
			if (!window.spores) {
				window.spores = [];
			}
			window.spores.push(spore);
		}
	};
	// 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;
			if (self.x >= leftEdge && self.x <= rightEdge) {
				if (Math.abs(self.y - (platform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
					// Add this check:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					// Add this check:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					onAnyPlatform = true;
					self.currentPlatform = platform;
					self.y = platform.y - ENEMY_PLATFORM_OFFSET;
					self.isOnGround = true;
					self.velocityY = 0;
					return true;
				}
				if (self.velocityY > 0 && self.y < platform.y - ENEMY_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - ENEMY_PLATFORM_OFFSET) {
					// Add this check here too:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					// Add this check here too:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					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;
			// Check if player has slide damage upgrade
			if (player.slideDamage && player.slideDamageTimer <= 0) {
				// If upgraded, treat as regular attack
				self.hitType = 'attack';
				player.slideDamageTimer = player.slideDamageCooldown;
			} else {
				// Otherwise just knockback
				self.hitType = 'slide';
			}
			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') {
				// (existing eyeball trajectory calculation)
				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;
				}
				self.velocityY = verticalTrajectory;
			}
			// Play the appropriate hit sound based on enemy type
			if (self.type === 'eyeball') {
				LK.getSound('eyeballhit').play();
			} else if (self.type === 'skeleton') {
				LK.getSound('skeletonhit').play();
			} else if (self.type === 'mushroom' && player.slideDamage) {
				LK.getSound('mushroomdie').play();
			} else if (self.type === 'mushroom') {
				LK.getSound('mushroombounce').play();
			} else {
				LK.getSound('enemyhit').play();
			}
			// 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
			}
			// Play appropriate hit sound based on enemy type
			if (self.type === 'mushroom') {
				LK.getSound('mushroomdie').play();
			}
			// 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;
		}
		// Apply loot multiplier from Fortune Finder upgrade
		var lootMultiplier = player.lootMultiplier || 1.0;
		// Determine number of coins to drop (1-5) adjusted by loot modifier and player's upgrade
		var coinCount = Math.floor(GameUtils.randomRange(1, 6) * self.lootModifier * lootMultiplier);
		// Small chance for jewels - increased by loot modifier AND player upgrade
		var dropJewel = Math.random();
		var jewelChance = 0.02 * self.lootModifier * lootMultiplier; // Base 2% chance multiplied by modifiers
		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 if (self.type === 'mushroom') {
			// Mushroom logic - modified to include attack state
			if (self.isHit) {
				self.updateMushroomHitState();
			} else if (self.isDying) {
				self.updateMushroomDyingState();
			} else if (self.isAttacking) {
				self.updateMushroomAttackState();
			} else {
				self.updateMushroomNormalState();
			}
		} 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 if (self.type === 'mushroom') {
		self.initMushroomSprites();
	} 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;
	// NEW METHOD: Update max health and add/remove heart sprites as needed
	self.updateMaxHealth = function (newMaxHealth) {
		var heartSpacing = 80;
		// Add new hearts if needed
		if (newMaxHealth > self.maxHealth) {
			for (var i = self.maxHealth; i < newMaxHealth; i++) {
				var heart = self.attachAsset('heart', {
					anchorX: 0.5,
					anchorY: 0.5,
					tint: 0xCC0000
				});
				self.hearts.push(heart);
			}
		}
		// Remove hearts if needed (unlikely, but for completeness)
		else if (newMaxHealth < self.maxHealth) {
			for (var i = self.maxHealth - 1; i >= newMaxHealth; i--) {
				if (self.hearts[i]) {
					self.hearts[i].destroy();
					self.hearts.pop();
				}
			}
		}
		// Update max health value
		self.maxHealth = newMaxHealth;
		// Recalculate total width and reposition all hearts
		totalWidth = (self.maxHealth - 1) * heartSpacing;
		for (var i = 0; i < self.hearts.length; i++) {
			self.hearts[i].x = i * heartSpacing - totalWidth / 2;
		}
	};
	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) {
			// Get loot multiplier from player
			var lootMultiplier = player.lootMultiplier || 1.0;
			// Spawn health potion with low chance - improved by loot multiplier
			if (Math.random() < 0.05 * (player.potionDropChance || 1.0)) {
				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 - improved by loot multiplier
			if (Math.random() < 0.25 * (player.arrowDropChance || 1.0)) {
				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 - quantity affected by loot multiplier
			var coinCount = Math.floor(GameUtils.randomRange(1, 9) * lootMultiplier);
			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;
});
var CrumblePlatform = Platform.expand(function () {
	var self = Platform.call(this);
	// Override the sprite
	self.sprite = self.attachAsset('crumbleplatform', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.originalY = 0;
	// Crumbling state
	self.isCrumbling = false;
	self.crumbleDelay = 60; // frames before breaking
	self.crumbleTimer = 0;
	self.triggered = false;
	self.broken = false; // Add this flag to prevent multiple breaks
	// Override update method
	self.update = function () {
		// Normal platform movement
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		// Handle crumbling state
		if (self.isCrumbling && !self.broken) {
			// Quick shake effect
			var shakeIntensity = Math.min(6, 2 + (self.crumbleDelay - self.crumbleTimer) / 5);
			self.sprite.y = Math.random() * shakeIntensity * 2 - shakeIntensity;
			self.crumbleTimer--;
			// Only break when the timer is actually finished
			if (self.crumbleTimer <= 0) {
				self["break"]();
			}
		}
		// Check if off screen
		if (self.x < -500) {
			self.destroy();
		}
	};
	self.trigger = function () {
		if (!self.triggered && !self.broken) {
			LK.getSound('platformcrumble').play();
			self.triggered = true;
			self.isCrumbling = true;
			self.crumbleTimer = self.crumbleDelay;
			self.sprite.originalY = self.sprite.y;
		}
	};
	self["break"] = function () {
		if (self.broken) {
			return;
		}
		self.broken = true;
		LK.getSound('rocksfall').play();
		// CRITICAL FIX: Remove from platforms array BEFORE updating entities
		var index = platforms.indexOf(self);
		if (index !== -1) {
			platforms.splice(index, 1);
		}
		// Force all entities on this platform to fall
		if (player && player.currentPlatform === self) {
			player.isOnGround = false;
			player.currentPlatform = null;
			player.velocityY = 3; // Stronger initial velocity
		}
		// Check all enemies too
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].currentPlatform === self) {
				enemies[i].isOnGround = false;
				enemies[i].currentPlatform = null;
				enemies[i].velocityY = 1;
			}
		}
		// Spawn 10 pieces distributed across the platform width
		for (var i = 0; i < 10; i++) {
			var pieceNum = Math.floor(Math.random() * 3) + 1;
			var piece = new CrumblePiece(pieceNum);
			// Distribute pieces across the platform width
			var distributionOffset = i / 9 * PLATFORM_WIDTH - PLATFORM_HALF_WIDTH;
			piece.x = self.x + distributionOffset + (Math.random() * 80 - 40); // Add small random offset
			piece.y = self.y + (Math.random() * 30 - 15); // Small vertical variation
			// Vary the falling speeds and behaviors
			piece.velocityX = -PLATFORM_SPEED + (Math.random() * 2 - 1); // Slight x variation
			piece.velocityY = Math.random() * 3; // Some pieces fall faster than others
			piece.rotationSpeed = (Math.random() - 0.5) * 0.25; // More varied rotation
			game.addChild(piece);
		}
		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'];
	self.airDashAnimation = ['playerairdash'];
	self.groundSlamAnimation = ['playergroundslam1', 'playergroundslam2', 'playergroundslam3', 'playergroundslam4', 'playergroundslam5', 'playergroundslam6'];
	// 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.092;
	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 = 100;
	self.standUpDuration = 30;
	self.slideCooldown = 30;
	self.lastSlideTime = 0;
	self.slideSpeedMultiplier = 1.75;
	self.slideDamage = false; // Will be set based on upgrade
	self.slideDamageCooldown = 15; // Frames between slide damage hits
	self.slideDamageTimer = 0; // Current cooldown timer
	self.normalHitboxHeight = self.hitboxHeight; // Store original height
	self.slideHitboxHeight = 200; // Make it taller
	self.slideHitboxYOffset = 150; // Move it much lower
	self.isAirDashing = false;
	self.canAirDash = true; // Reset when landing
	self.airDashTimer = 0;
	self.airDashDuration = 100; // Half of slide duration since it's faster
	self.airDashSpeedMultiplier = 2.3; // Slightly faster than slide
	self.shadowImages = [];
	// Add ground slam properties
	self.isGroundSlamming = false;
	self.groundSlamFrame = 0;
	self.groundSlamPhase = "start"; // "start", "air", "impact"
	self.canGroundSlam = true; // Can only ground slam once per jump
	self.groundSlamAnimationSpeed = 0.15;
	self.groundSlamImpactTimer = 0;
	// Initialize animation sprites
	self.initAnimations = function () {
		// Run animations
		for (var i = 0; i < self.runAnimation.length; i++) {
			var sprite = self.attachAsset(self.runAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = i === 0 ? 1 : 0;
			self.sprites.push(sprite);
		}
		// Jump animations
		for (var i = 0; i < self.jumpAnimation.length; i++) {
			var sprite = self.attachAsset(self.jumpAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Attack animations
		for (var i = 0; i < self.attackAnimation.length; i++) {
			var sprite = self.attachAsset(self.attackAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// 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);
		}
		// Add air dash animation
		for (var i = 0; i < self.airDashAnimation.length; i++) {
			var sprite = self.attachAsset(self.airDashAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		for (var i = 0; i < self.groundSlamAnimation.length; i++) {
			var sprite = self.attachAsset(self.groundSlamAnimation[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];
			// Use EXACT platform bounds for edge detection - no added buffer
			var leftEdge = platform.x - PLATFORM_HALF_WIDTH;
			var rightEdge = platform.x + PLATFORM_HALF_WIDTH;
			// Check if player is within horizontal bounds of platform
			if (self.x >= leftEdge && self.x <= rightEdge) {
				// If we're at platform height and not jumping
				if (Math.abs(self.y - (platform.y - PLAYER_PLATFORM_OFFSET)) < 5 && !self.isJumping) {
					onAnyPlatform = true;
					// Add this check for crumble platforms
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					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 - use EXACT edge detection
		if (self.currentPlatform) {
			// Add check for destroyed platform
			if (self.currentPlatform.destroyed || self.currentPlatform.broken) {
				self.isOnGround = false;
				self.currentPlatform = null;
				if (self.velocityY === 0) {
					self.velocityY = 0.1;
				}
				return;
			}
			// Use EXACT platform bounds - no buffer for edge detection when moving
			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;
					}
					// Use EXACT platform bounds for other platforms too
					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 () {
		// First, check if current platform reference is valid
		if (self.currentPlatform && (self.currentPlatform.destroyed || self.currentPlatform instanceof CrumblePlatform && self.currentPlatform.broken)) {
			// Force falling state if platform reference is to a broken/destroyed platform
			self.isOnGround = false;
			self.currentPlatform = null;
			if (self.velocityY < 1) {
				self.velocityY = 1; // Ensure downward movement begins
			}
		}
		// Then apply normal physics
		if (!self.isOnGround || self.isJumping) {
			// Apply velocity and gravity
			self.y += self.velocityY;
			self.velocityY += 0.89; // Increase gravity effect
			// Check for landing on platforms
			var hitPlatform = self.checkPlatformCollision();
		}
		// 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
			SaveManager.save();
			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;
			// Create dust particles behind player during slide (every 5 frames)
			if (LK.ticks % 5 === 0) {
				// Create 2-3 particles per emission
				var particleCount = 2 + Math.floor(Math.random() * 2);
				for (var i = 0; i < particleCount; i++) {
					var particle = game.addChild(LK.getAsset('pixel', {
						anchorX: 0.5,
						anchorY: 0.5,
						scaleX: 0.1 + Math.random() * 0.2,
						scaleY: 0.1 + Math.random() * 0.2,
						alpha: 0.7 + Math.random() * 0.3
					}));
					// Position particles behind player near ground level
					particle.x = self.x + 120 - Math.random() * 50;
					particle.y = self.y + 145 + Math.random() * 40;
					// Set velocities with slight variation
					particle.velocityX = -Math.random() * 3 - 1;
					particle.velocityY = -Math.random() * 2;
					// Add slight random rotation
					particle.rotationSpeed = (Math.random() - 0.5) * 0.1;
					particle.lifespan = 20 + Math.floor(Math.random() * 15);
					// Add update method to particles
					particle.update = function () {
						this.x += this.velocityX;
						this.y += this.velocityY;
						this.rotation += this.rotationSpeed;
						this.lifespan--;
						if (this.lifespan < 10) {
							this.alpha -= 0.1;
						}
						if (this.lifespan <= 0) {
							this.destroy();
						}
					};
				}
			}
		} 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.updateAirDashAnimation = function () {
		// Calculate offset to find air dash animation sprite
		var airDashOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length + self.deathAnimation.length + self.bowAnimation.length;
		// Show air dash sprite
		self.sprites[airDashOffset].alpha = 1;
		// Add quick clockwise rotation during air dash
		if (self.airDashTimer === self.airDashDuration) {
			// Reset rotation at start of dash
			self.rotation = 0;
			// Play air dash sound effect
			LK.getSound('airdash').play();
			// Start rotation tween - make it happen faster and then hold value
			tween(self, {
				rotation: Math.PI / 8 // Rotate about 30 degrees clockwise
			}, {
				duration: 100,
				// Much faster rotation (300ms instead of ~1.5s)
				easing: tween.easeOut
				// No onFinish needed since we'll hold this rotation for the dash duration
			});
		}
		// Keep vertical velocity at 0 during dash
		self.velocityY = 0;
		// Update shadow positions with slight motion effect
		self.shadowImages.forEach(function (shadow, index) {
			// Add slight vertical variation to shadows for more dynamic feel
			var verticalOffset = Math.sin(self.airDashTimer * 0.1 + index * 0.5) * 10;
			// Adjust spacing and position for a more dramatic trail effect
			shadow.x = self.x - (80 * (index + 1) + (self.airDashDuration - self.airDashTimer) * 0.5);
			shadow.y = self.y + verticalOffset;
			shadow.alpha = 0.3 - index * 0.05; // More gradual fade out for longer trails
			// Add slight rotation to shadows
			shadow.rotation = self.rotation * (0.6 - index * 0.1); // Less rotation for farther shadows
		});
		self.airDashTimer--;
		if (self.airDashTimer <= 0) {
			self.isAirDashing = false;
			gameSpeedMultiplier = 1.0;
			// Reset rotation back to normal when dash ends
			tween(self, {
				rotation: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
			// Clean up shadows
			self.shadowImages.forEach(function (shadow) {
				if (shadow.parent) {
					shadow.destroy();
				}
			});
			self.shadowImages = [];
		}
	};
	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;
					arrow.startX = arrow.x;
					arrow.startY = arrow.y;
					arrow.rotation = 0; // Default right direction for tutorial
					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;
					arrow.startX = arrow.x;
					arrow.startY = arrow.y;
					// Smart initial aiming for tracking arrows
					var initialRotation = 0; // Default straight right
					if (UPGRADE_REGISTRY.tracking_arrow > 0 && enemies && enemies.length > 0) {
						// Find the best target within a reasonable cone
						var bestTarget = null;
						var bestScore = -1;
						var coneAngle = Math.PI / 3; // 60 degree cone (30 degrees up/down from horizontal)
						var maxDistance = 1000; // Maximum distance to consider for initial aiming
						for (var i = 0; i < enemies.length; i++) {
							var enemy = enemies[i];
							if (!enemy || enemy.isDying || enemy.isHit || enemy.destroyed || enemy.x < self.x) {
								continue;
							}
							var dx = enemy.x - arrow.x;
							var dy = enemy.y - arrow.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							var angleToEnemy = Math.atan2(dy, dx);
							// Check if enemy is within cone and reasonable distance
							if (distance <= maxDistance && Math.abs(angleToEnemy) <= coneAngle / 2) {
								// Score based on proximity and how centered they are in the cone
								var centeredness = 1 - Math.abs(angleToEnemy) / (coneAngle / 2);
								var proximity = 1 - distance / maxDistance;
								var score = centeredness * 0.6 + proximity * 0.4;
								if (score > bestScore) {
									bestScore = score;
									bestTarget = enemy;
								}
							}
						}
						// If we found a good target, aim at it initially
						if (bestTarget) {
							initialRotation = Math.atan2(bestTarget.y - arrow.y, bestTarget.x - arrow.x);
						}
					}
					arrow.rotation = initialRotation;
					arrow.sprite.rotation = initialRotation;
					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.updateGroundSlamAnimation = function () {
		// Calculate offset to find ground slam animation sprites
		var groundSlamOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length + self.deathAnimation.length + self.bowAnimation.length + self.airDashAnimation.length;
		// Create hitbox for the slam (used both in air and on impact)
		var slamHitbox = {
			left: self.x - 200,
			right: self.x + 200,
			top: self.y,
			bottom: self.y + 200
		};
		// Update animation based on phase
		if (self.groundSlamPhase === "start") {
			// Show first frame briefly
			self.sprites[groundSlamOffset].alpha = 1;
			self.animationCounter += self.attackAnimationSpeed;
			if (self.animationCounter >= 1) {
				self.groundSlamPhase = "air";
				self.animationCounter = 0;
			}
		} else if (self.groundSlamPhase === "air") {
			// Alternate between frames 2 and 3 while falling
			self.animationCounter += self.attackAnimationSpeed;
			if (self.animationCounter >= 1) {
				self.animationCounter = 0;
				self.groundSlamFrame = (self.groundSlamFrame + 1) % 2;
			}
			self.sprites[groundSlamOffset + 1 + self.groundSlamFrame].alpha = 1;
			// Check for enemies and breakables during descent
			self.checkGroundSlamHits(slamHitbox);
			// Check if we've hit the ground
			if (self.isOnGround) {
				self.groundSlamPhase = "impact";
				self.groundSlamFrame = 0;
				self.groundSlamImpactTimer = 30;
				self.animationCounter = 0;
				// Apply screen shake effect on impact
				applyScreenShake();
				LK.getSound('groundimpact').play();
				// Do one final more powerful hit check on impact
				self.applyGroundSlamDamage();
			}
		} else if (self.groundSlamPhase === "impact") {
			// Cycle through frames 4-6 for impact
			self.animationCounter += self.attackAnimationSpeed * 1.5;
			if (self.animationCounter >= 1) {
				self.animationCounter = 0;
				self.groundSlamFrame++;
				if (self.groundSlamFrame >= 3) {
					self.isGroundSlamming = false;
					return;
				}
			}
			self.sprites[groundSlamOffset + 3 + self.groundSlamFrame].alpha = 1;
			// Count down timer
			self.groundSlamImpactTimer--;
			if (self.groundSlamImpactTimer <= 0) {
				self.isGroundSlamming = false;
			}
		}
	};
	self.applyGroundSlamDamage = function () {
		// Create larger hitbox for ground slam
		var slamHitbox = {
			left: self.x - 300,
			right: self.x + 300,
			top: self.y,
			bottom: self.y + 200
		};
		// Add particle spray effect when landing
		self.createGroundSlamParticles();
		// Check all enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (!enemy.isHit && !enemy.isDying) {
				var enemyBounds = enemy.getBounds();
				// If enemy is on ground and in range
				if (GameUtils.checkCollision(slamHitbox, enemyBounds) && (enemy.isOnGround || enemy.y > self.y)) {
					enemy.hit();
					// Play appropriate sound
					if (enemy.type === 'eyeball') {
						LK.getSound('eyeballhit').play();
					} else if (enemy.type === 'skeleton') {
						LK.getSound('skeletonhit').play();
					} else {
						LK.getSound('enemyhit').play();
					}
					// Create more particles for impact
					particleSystem.emitFromHit(enemy.x + 250, enemy.y, self.x, 5, enemy.type);
				}
			}
		}
	};
	// Add a new method to create ground slam particles
	self.createGroundSlamParticles = function () {
		// Create a large number of particles for dramatic effect
		var particleCount = 25 + Math.floor(Math.random() * 10);
		for (var i = 0; i < particleCount; i++) {
			var particle = game.addChild(LK.getAsset('pixel', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.1 + Math.random() * 0.3,
				scaleY: 0.1 + Math.random() * 0.3,
				alpha: 0.7 + Math.random() * 0.3,
				// Keep particles untinted (white)
				tint: 0xFFFFFF
			}));
			// Position particles at the point of impact
			particle.x = self.x + Math.random() * 300 - 150; // Spread horizontally
			particle.y = self.y + 145; // Just above ground level
			// Set velocities with upward trajectory
			var angle = Math.PI / 2 + (Math.random() - 0.5) * 0.8; // Mostly upward with spread
			var speed = 3 + Math.random() * 9; // Varying speeds
			particle.velocityX = Math.cos(angle) * speed * (Math.random() > 0.5 ? 1 : -1);
			particle.velocityY = -Math.sin(angle) * speed; // Negative for upward
			// Add slight random rotation
			particle.rotationSpeed = (Math.random() - 0.5) * 0.2;
			// Longer lifespan for more dramatic effect
			particle.lifespan = 25 + Math.floor(Math.random() * 20);
			// Add update method to particles
			particle.update = function () {
				this.x += this.velocityX;
				this.y += this.velocityY;
				this.velocityY += 0.3; // Apply gravity
				this.rotation += this.rotationSpeed;
				this.lifespan--;
				// Start fading out near end of life
				if (this.lifespan < 10) {
					this.alpha -= 0.1;
				}
				if (this.lifespan <= 0 || this.alpha <= 0) {
					this.destroy();
				}
			};
		}
	};
	self.checkGroundSlamHits = function (slamHitbox) {
		// Check enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (!enemy.isHit && !enemy.isDying) {
				var enemyBounds = enemy.getBounds();
				if (GameUtils.checkCollision(slamHitbox, enemyBounds)) {
					enemy.hit();
					// Play appropriate sound
					if (enemy.type === 'eyeball') {
						LK.getSound('eyeballhit').play();
					} else if (enemy.type === 'skeleton') {
						LK.getSound('skeletonhit').play();
					} else {
						LK.getSound('enemyhit').play();
					}
					// Create particles
					particleSystem.emitFromHit(enemy.x + 250, enemy.y, self.x, 5, enemy.type);
				}
			}
		}
		// Check for breakable objects (jars and chests)
		for (var i = collectibles.length - 1; i >= 0; i--) {
			var collectible = collectibles[i];
			// Only check jars and chests
			if (collectible instanceof Jar || collectible instanceof TreasureChest) {
				var itemBounds = {
					left: collectible.x - 50,
					right: collectible.x + 50,
					top: collectible.y - 75,
					bottom: collectible.y + 75
				};
				if (GameUtils.checkCollision(slamHitbox, itemBounds)) {
					collectible["break"]();
					collectibles.splice(i, 1);
				}
			}
		}
	};
	// Modified applyLandingSquash function
	self.applyLandingSquash = function () {
		// More extreme initial squash values for better visibility
		var initialScaleX = 1.2;
		var initialScaleY = 0.8;
		var targetScaleX = 1.0;
		var targetScaleY = 1.0;
		var duration = 370; // ms - slightly longer duration
		// Apply squash to the entire player container instead of individual sprites
		self.scale.set(initialScaleX, initialScaleY);
		// Clear any existing scale tweens
		tween.stop(self.scale, {
			x: true,
			y: true
		});
		// Tween back to normal scale with a slight delay
		LK.setTimeout(function () {
			tween(self.scale, {
				x: targetScaleX,
				y: targetScaleY
			}, {
				duration: duration,
				easing: tween.elasticOut // Keep the bouncier easing
			});
		}, 90); // Slightly longer delay
	};
	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 () {
		// Update timer first
		if (self.brokenPlatformTimer > 0) {
			self.brokenPlatformTimer--;
		}
		for (var i = 0; i < platforms.length; i++) {
			var platform = platforms[i];
			// Skip platforms that are broken or destroyed
			if (platform.broken || platform.destroyed) {
				continue;
			}
			// Skip this specific platform if it's the one that just broke
			if (self.brokenPlatformTimer > 0 && platform === self.lastBrokenPlatform) {
				continue;
			}
			// Keep the extra buffer for landing on platforms for a smoother experience
			// but ensure it's consistent with other checks
			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 && self.x <= platform.x + PLATFORM_HALF_WIDTH) {
				// Add this check for crumble platforms
				if (platform instanceof CrumblePlatform) {
					platform.trigger();
				}
				self.y = platform.y - PLAYER_PLATFORM_OFFSET;
				self.velocityY = 0;
				self.isJumping = false;
				self.isOnGround = true;
				self.currentPlatform = platform;
				self.applyLandingSquash();
				if (self.isOnGround) {
					self.canAirDash = true;
					self.canGroundSlam = true;
					// Clear any remaining shadow images
					self.shadowImages.forEach(function (shadow) {
						if (shadow.parent) {
							shadow.destroy();
						}
					});
					self.shadowImages = [];
				}
				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 () {
		// Add a timestamp check specifically for tutorial
		if (!self.lastJumpTime) {
			self.lastJumpTime = 0;
		}
		var currentTime = Date.now();
		if (tutorialActive && currentTime - self.lastJumpTime < 300) {
			return; // Block rapid jumps in tutorial
		}
		self.lastJumpTime = currentTime;
		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;
			// Play slide sound effect
			LK.getSound('slide').play();
			// Adjust game speed multiplier instead of base speed
			gameSpeedMultiplier = self.slideSpeedMultiplier;
		}
	};
	self.airDash = function () {
		if (!self.canAirDash || self.isOnGround || self.isSliding || self.isAttacking || self.isShooting || self.isAirDashing) {
			return;
		}
		self.isAirDashing = true;
		self.airDashTimer = self.airDashDuration;
		self.canAirDash = false;
		self.velocityY = 0;
		// Create shadow images - increased from 2 to 4 for more dramatic effect
		for (var i = 0; i < 4; i++) {
			var shadow = game.addChild(LK.getAsset('playerairdash', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0.3 - i * 0.05,
				// Adjusted alpha reduction to spread across more shadows
				x: self.x - 80 * (i + 1),
				// Adjusted spacing to create a tighter trail
				y: self.y
			}));
			self.shadowImages.push(shadow);
		}
		gameSpeedMultiplier = self.airDashSpeedMultiplier;
	};
	// 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.groundSlam = function () {
		if (!self.hasGroundSlam) {
			return;
		}
		// Only allow if in air, not already slamming, and can slam
		if (!self.isOnGround && !self.isGroundSlamming && self.canGroundSlam && !self.isSliding && !self.isAttacking && !self.isAirDashing && !self.isShooting) {
			self.isGroundSlamming = true;
			self.canGroundSlam = false; // Use up the slam for this jump
			self.groundSlamFrame = 0;
			self.groundSlamPhase = "start";
			self.animationCounter = 0;
			// Boost downward velocity
			self.velocityY = 15; // Strong downward force
			// Play sound effect (can use sword slash for now)
			LK.getSound('groundsmashfalling').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);
			// Existing damage code...
			if (self.currentHealth <= 0) {
				// Save before game over
				SaveManager.save();
				// 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 (self.slideDamageTimer > 0) {
			self.slideDamageTimer--;
		}
		if (self.isAirDashing) {
			// Update air dash animation
			self.updateAirDashAnimation();
		}
		// 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
		if (!self.isAirDashing) {
			self.applyPhysics();
		}
		// Handle platform collision and falling
		self.handlePlatformCollision();
		// Handle invulnerability
		self.updateInvulnerability();
		// Update animations based on state
		if (self.isGroundSlamming) {
			self.updateGroundSlamAnimation();
		} else if (self.isAirDashing) {
			self.updateAirDashAnimation();
		} else 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 using CustomText instead of Text2
	self.text = new CustomText('+' + amount, {
		size: 70,
		fill: 0xFFFFFF
	});
	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 ShopManager = Container.expand(function () {
	var self = Container.call(this);
	self.categories = ['Health', 'Combat', 'Ranged', 'Loot', 'Spells'];
	self.currentCategory = 'Health';
	self.currentUpgradeIndex = 0; // Track current upgrade in category
	self.categoryUpgrades = []; // Store current category's upgrades
	self.selectedUpgrade = null;
	// Define upgrades with proper effects - no storage modification
	self.upgrades = [{
		id: 'health_increase',
		category: 'Health',
		title: 'Heart Container',
		description: 'Increase maximum health by one heart',
		basePrice: 300,
		maxLevel: 2,
		icon: 'health_increase_icon',
		// Effect only for immediate visual feedback in shop
		effect: function effect(level) {
			console.log("Health upgrade purchased: level " + level);
			// Could update preview if needed
		}
	}, {
		id: 'arrow_capacity',
		category: 'Ranged',
		title: 'Quiver Expansion',
		description: 'Start with more arrows',
		basePrice: 100,
		maxLevel: 5,
		icon: 'arrow_capacity_icon',
		effect: function effect(level) {
			console.log("Arrow capacity upgrade purchased: level " + level);
		}
	},
	// --- Tracking Arrow upgrade inserted here ---
	{
		id: 'tracking_arrow',
		category: 'Ranged',
		title: 'Magic Arrow',
		description: 'Arrows gain basic homing capabilities and can ricochet to nearby enemies',
		basePrice: 1200,
		maxLevel: 1,
		icon: 'arrow_finder_icon',
		effect: function effect(level) {
			console.log("Tracking Arrow upgrade purchased: level " + level);
		}
	},
	// --- End Tracking Arrow upgrade ---
	{
		id: 'levitate',
		category: 'Spells',
		title: 'Levitate',
		description: 'Coming soon.',
		basePrice: 99999,
		maxLevel: 0,
		icon: 'levitate_icon',
		effect: function effect(level) {
			console.log("Levitate upgrade purchased: level " + level);
		}
	}, {
		id: 'slide_damage',
		category: 'Combat',
		title: 'Crushing Slide',
		description: 'Sliding now damages enemies',
		basePrice: 1000,
		maxLevel: 1,
		icon: 'icon_combat',
		effect: function effect(level) {
			console.log("Slide damage upgrade purchased: level " + level);
		}
	}, {
		id: 'ground_slam',
		category: 'Combat',
		title: 'Ground Slam',
		description: 'Smash downward by swiping down from the air to damage enemies below',
		basePrice: 1000,
		maxLevel: 1,
		icon: 'icon_combat',
		effect: function effect(level) {
			console.log("Ground slam upgrade purchased: level " + level);
		}
	}, {
		id: 'better_loot',
		category: 'Loot',
		title: 'Fortune Finder',
		description: 'Increase chances of finding valuable loot',
		basePrice: 400,
		maxLevel: 4,
		icon: 'icon_treasure',
		effect: function effect(level) {}
	}, {
		id: 'potion_chance',
		category: 'Health',
		title: 'Potion Finder',
		description: 'Increases the chance of health potions dropping from breakable objects',
		basePrice: 300,
		// Cheaper than health container (600)
		maxLevel: 3,
		icon: 'healthpotion',
		effect: function effect(level) {}
	}, {
		id: 'arrow_finder',
		category: 'Ranged',
		title: 'Arrow Finder',
		description: 'Increases the chance of arrows dropping from breakable objects',
		basePrice: 300,
		maxLevel: 3,
		icon: 'arrow_capacity_icon',
		effect: function effect(level) {}
	}, {
		id: 'loot_magnet',
		category: 'Loot',
		title: 'Loot Magnet',
		description: 'Increases collection range for coins and items',
		basePrice: 300,
		maxLevel: 3,
		icon: 'icon_treasure',
		effect: function effect(level) {
			console.log("Loot magnet upgrade purchased: level " + level);
		}
	}];
	// Create tabs
	self.createTabs();
	// Create upgrade list
	self.upgradeList = new Container();
	self.addChild(self.upgradeList);
	// Create buy button
	self.createBuyButton();
	self.initPlayerAnimation();
	// Initial setup
	self.showCategory('Health');
	self.createArrows();
	return self;
});
var ShopUpgrade = Container.expand(function (config) {
	var self = Container.call(this);
	// Keep all existing properties
	self.id = config.id;
	self.category = config.category;
	self.title = config.title;
	self.description = config.description;
	self.basePrice = config.basePrice;
	self.level = 0;
	self.maxLevel = config.maxLevel || 1;
	self.icon = config.icon;
	self.effect = config.effect;
	// Create a container for the window contents
	self.windowContainer = new Container();
	self.addChild(self.windowContainer);
	// Add background to the container
	self.background = self.windowContainer.attachAsset('shop_window', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.iconSprite = self.windowContainer.attachAsset(self.id + '_icon', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.iconSprite.x = -330;
	self.iconSprite.y = -500;
	self.titleText = new CustomText(self.title, {
		size: 50,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.windowContainer.addChild(self.titleText);
	self.titleText.x = -200;
	self.titleText.y = -500;
	self.descriptionText = new CustomText(self.description + '.', {
		size: 45,
		fill: 0xA89F8A,
		letterSpacing: 5,
		wordWrapWidth: 800,
		wordWrap: true
	});
	self.windowContainer.addChild(self.descriptionText);
	self.descriptionText.x = -370;
	self.descriptionText.y = -350;
	self.levelText = new CustomText('LEVEL ' + self.level + (self.level >= self.maxLevel ? ' MAX' : ''), {
		size: 60,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.windowContainer.addChild(self.levelText);
	self.levelText.x = -370;
	self.levelText.y = 530;
	// Methods with proper separation of concerns
	self.getCurrentPrice = function () {
		return self.basePrice * Math.pow(2, self.level);
	};
	self.canPurchase = function (gold) {
		return self.level < self.maxLevel && gold >= self.getCurrentPrice();
	};
	self.purchase = function () {
		if (self.level < self.maxLevel) {
			this.level++;
			// UPDATE THE REGISTRY
			switch (this.id) {
				case 'health_increase':
					UPGRADE_REGISTRY.health_increase = this.level;
					break;
				case 'arrow_capacity':
					UPGRADE_REGISTRY.arrow_capacity = this.level;
					break;
				case 'levitate':
					UPGRADE_REGISTRY.levitate = this.level;
					break;
				case 'slide_damage':
					UPGRADE_REGISTRY.slide_damage = this.level;
					break;
				case 'better_loot':
					UPGRADE_REGISTRY.better_loot = this.level;
					break;
				case 'potion_chance':
					UPGRADE_REGISTRY.potion_chance = this.level;
					break;
				case 'arrow_finder':
					UPGRADE_REGISTRY.arrow_finder = this.level;
					break;
				case 'ground_slam':
					UPGRADE_REGISTRY.ground_slam = this.level;
					break;
				case 'loot_magnet':
					UPGRADE_REGISTRY.loot_magnet = this.level;
					break;
				case 'tracking_arrow':
					UPGRADE_REGISTRY.tracking_arrow = this.level;
					break;
			}
			// Update UI text
			this.levelText.setText('LEVEL ' + this.level + (this.level >= this.maxLevel ? ' MAX' : ''));
			// Apply the effect for immediate feedback
			this.effect(this.level);
			return true;
		}
		return false;
	};
});
// Add ShopUpgrade.prototype methods here
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 with default values
	self.speed = 6;
	self.rotationSpeed = -0.1;
	self.constantVelocity = 8; // Can be overridden when created
	// Update method called every frame
	self.update = function () {
		// Use constantVelocity if set, otherwise fall back to speed * gameSpeedMultiplier
		var velocity = self.constantVelocity || self.speed * gameSpeedMultiplier;
		// Move the sword
		self.x -= velocity;
		// 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;
});
var Spore = Container.expand(function () {
	var self = Container.call(this);
	// Animation properties
	self.frame = 0;
	self.animationCounter = 0;
	self.animationSpeed = 0.035;
	self.sprites = [];
	// Physics properties
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifespan = 180; // Increased lifespan
	self.initialSpeed = 4.0; // Slower initial speed
	self.speed = self.initialSpeed;
	self.damage = 1;
	self.isExploding = false;
	self.hasHitPlayer = false;
	self.isActive = true; // New flag to track if this spore is still active for collision
	// Initialize animation sprites
	for (var i = 1; i <= 8; i++) {
		var sprite = self.attachAsset('spore' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 1.3,
			scaleY: 1.3
		});
		sprite.alpha = i === 1 ? 1 : 0;
		self.sprites.push(sprite);
	}
	// Get collision bounds - only return bounds if the spore is active
	self.getBounds = function () {
		if (!self.isActive) {
			// Return a dummy bounds that can't possibly collide with anything
			return {
				left: -9999,
				right: -9999,
				top: -9999,
				bottom: -9999
			};
		}
		return {
			left: self.x - 40,
			right: self.x + 40,
			top: self.y - 40,
			bottom: self.y + 40
		};
	};
	// Override the destroy method to ensure proper cleanup
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.isActive = false; // Mark as inactive first
		// Remove from global spores array if it exists
		if (window.spores) {
			var index = window.spores.indexOf(self);
			if (index !== -1) {
				window.spores.splice(index, 1);
			}
		}
		// Call the original destroy method
		originalDestroy.call(self);
	};
	// Hide all sprites
	self.hideAllSprites = function () {
		for (var i = 0; i < self.sprites.length; i++) {
			self.sprites[i].alpha = 0;
		}
	};
	// Start explosion animation
	self.explode = function () {
		if (!self.isExploding) {
			self.isExploding = true;
			self.frame = 4; // Start at explosion frame
			self.lifespan = Math.min(self.lifespan, 30); // Short explosion time
			// Stop movement
			self.velocityX *= 0.3;
			self.velocityY *= 0.3;
			// Deactivate the spore for collision once explosion starts
			self.isActive = false;
		}
	};
	// Update method
	self.update = function () {
		// Hide all sprites first
		self.hideAllSprites();
		// Move spore
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Apply platform movement to match world movement
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		// Update animation
		self.animationCounter += self.animationSpeed;
		if (self.animationCounter >= 1) {
			self.animationCounter = 0;
			self.frame = (self.frame + 1) % self.sprites.length;
		}
		self.sprites[self.frame].alpha = 1;
		// Decrease lifespan
		self.lifespan--;
		// Start fading out near end of life
		if (self.lifespan < 30) {
			self.alpha = self.lifespan / 30;
		}
		// Destroy if off screen or lifespan ended
		if (self.x < -100 || self.x > GAME_WIDTH + 100 || self.y < -100 || self.y > GAME_HEIGHT + 100 || self.lifespan <= 0) {
			self.destroy();
		}
	};
	return self;
});
/**** 
* Constants
****/ 
var Torch = Container.expand(function () {
	var self = Container.call(this);
	// Create base torch sprite
	self.base = self.attachAsset('torch', {
		anchorX: 0.5,
		anchorY: 1
	});
	// Create flame sprite
	self.flame = self.attachAsset('torchflame', {
		anchorX: 0.5,
		anchorY: 1,
		y: -180
	});
	// Create aura sprite
	self.aura = self.attachAsset('torchaura', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.3,
		y: -250
	});
	// Animation properties
	self.flameTime = Math.random() * Math.PI * 2;
	self.auraTime = Math.random() * Math.PI * 2;
	self.flameSpeed = 0.05;
	self.auraSpeed = 0.03;
	// Update animation
	self.update = function () {
		// Animate flame scale
		self.flameTime += self.flameSpeed;
		var flameScale = 1 + Math.sin(self.flameTime) * 0.2;
		self.flame.scaleY = flameScale;
		// Random flip chance for flame
		if (Math.random() < 0.02) {
			self.flame.scaleX *= -1;
		}
		// Animate aura alpha
		self.auraTime += self.auraSpeed;
		var auraAlpha = 0.3 + Math.sin(self.auraTime) * 0.15;
		self.aura.alpha = auraAlpha;
	};
	return self;
});
var TreasureChest = Container.expand(function () {
	var self = Container.call(this);
	// Attach chest sprite
	self.sprite = self.attachAsset('treasurechest', {
		anchorX: 0.5,
		anchorY: 0.5,
		tint: 0xC0C0C0
	});
	// Initialize as breakable
	BreakableBehavior.initBreakable(self);
	// Break functionality
	self["break"] = function () {
		BreakableBehavior.standardBreak(self, TreasureChestPiece, 4, function (chest) {
			// Get loot multiplier from player
			var lootMultiplier = player.lootMultiplier || 1.0;
			// Spawn health potion with medium chance - improved by loot multiplier
			if (Math.random() < 0.25 * (player.potionDropChance || 1.0)) {
				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 - improved by loot multiplier
			if (Math.random() < 0.40 * (player.arrowDropChance || 1.0)) {
				var arrowCount = Math.floor(GameUtils.randomRange(1, 4) * lootMultiplier);
				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 - quantity affected by loot multiplier
			var totalItems = Math.floor(GameUtils.randomRange(3, 9) * lootMultiplier);
			for (var i = 0; i < totalItems; i++) {
				// Random chance for different gems - probabilities improved by loot multiplier
				var rand = Math.random();
				var item;
				// Increase chances for better gems based on multiplier (cap at reasonable values)
				var diamondChance = Math.min(0.05 * lootMultiplier, 0.15);
				var emeraldChance = Math.min(0.15 * lootMultiplier, 0.25);
				var rubyChance = Math.min(0.30 * lootMultiplier, 0.40);
				if (rand < diamondChance) {
					item = new Coin('diamond');
				} else if (rand < emeraldChance) {
					item = new Coin('emerald');
				} else if (rand < rubyChance) {
					item = new Coin('ruby');
				} else {
					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',
		AIR_DASH: 'air_dash',
		COMPLETE: 'complete'
	}; //POG_REPLACE_LINE>{yq}
	// 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.hasAirDashSucceeded = false;
	self.firstJumpPerformed = false;
	self.secondJumpPerformed = false;
	// Message display
	self.messageContainer = new Container();
	self.messageText = new CustomText('', {
		size: 60,
		fill: 0xFFFFFF,
		letterSpacing: 5,
		wordWrap: true,
		wordWrapWidth: 1200,
		anchorX: 0.5
	});
	self.messageContainer.addChild(self.messageText);
	self.messageContainer.width = 1200;
	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") {
				// Add visual feedback animation
				tween(self.backButton, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(self.backButton, {
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 200,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								self.complete();
							}
						});
					}
				});
				// Play menu select sound
				LK.getSound('menuselect').play();
			}
		};
		self.backButton.down = self.backButtonDown;
		// Clear any existing enemies/arrows
		enemies = [];
		arrows = [];
		self.spawnTutorialEnemy();
	};
	// Message handling
	self.setMessage = function (text) {
		// First, remove the existing text
		if (self.messageText.parent) {
			self.messageContainer.removeChild(self.messageText);
		}
		// Create a new text object each time (like the shop does)
		self.messageText = new CustomText(text, {
			size: 60,
			fill: 0xFFFFFF,
			letterSpacing: 5,
			wordWrap: true,
			wordWrapWidth: 1200,
			anchorX: 0.5
		});
		// Add to container with fixed positioning
		self.messageContainer.addChild(self.messageText);
		self.messageText.x = 0;
		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;
				}
				if (self.hasSlideSucceeded && !player.isSliding) {
					self.currentState = self.states.AIR_DASH;
					self.setMessage("Swipe right while in the air to air dash!");
					self.hasSlideSucceeded = false;
					self.hasAirDashSucceeded = false;
					// Reset player state for air dashing
					player.canAirDash = true;
				}
				break;
			case self.states.AIR_DASH:
				// Simply check if the player has air dashed
				if (!self.hasAirDashSucceeded && player.isAirDashing) {
					self.hasAirDashSucceeded = true;
					// As soon as they air dash, consider the tutorial complete
					LK.setTimeout(function () {
						self.currentState = self.states.COMPLETE;
						self.setMessage("That's it!\nNow go get some treasure!");
						LK.setTimeout(function () {
							self.complete();
						}, 3000);
					}, 1000); // Wait 1 second after air dash before showing completion message
				}
				break;
		}
	};
	// Input handling
	// Inside TutorialManager's checkInput method, where it checks different states
	self.checkInput = function (startX, startY, endX, endY) {
		if (!gameStarted || player.isDying) {
			return;
		}
		// Initialize the jump tracking property if it doesn't exist
		if (!self.inputProcessed) {
			self.inputProcessed = false;
		}
		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();
			}
			self.inputProcessed = true;
			return;
		}
		// Don't process the same input multiple times
		if (self.inputProcessed) {
			return;
		}
		self.inputProcessed = true;
		// Track last jump time to prevent multiple jumps from a single swipe
		if (!self.lastJumpTime) {
			self.lastJumpTime = 0;
		}
		var currentTime = Date.now();
		// 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();
			}
			// ADD THIS AIR DASH HANDLING RIGHT HERE
			else if (deltaX > 70 && self.currentState === self.states.AIR_DASH && !player.isOnGround) {
				player.airDash();
			}
		} else {
			if (deltaY < -120) {
				// Upward swipe with cooldown check
				if (currentTime - self.lastJumpTime > JUMP_COOLDOWN) {
					if (self.currentState === self.states.JUMP) {
						self.hasJumped = true;
						player.jump();
						self.lastJumpTime = currentTime;
					} else if (self.currentState === self.states.DOUBLE_JUMP) {
						self.hasDoubleJumped = true;
						player.jump();
						self.lastJumpTime = currentTime;
					} else if (self.currentState === self.states.AIR_DASH) {
						// Allow jumping during air dash tutorial too
						player.jump();
						self.lastJumpTime = currentTime;
					}
				}
			} else if (deltaY > 120 && 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
****/ 
//resetAllStorage();
// Add ShopUpgrade.prototype methods here
// Base collectible behavior for items that can be collected
/**** 
* Game Management
****/ 
initializeStorage();
ShopUpgrade.prototype.setSelected = function (isSelected) {
	var self = this;
	// Find the background asset within windowContainer children
	var background = null;
	for (var i = 0; i < self.windowContainer.children.length; i++) {
		if (self.windowContainer.children[i].asset && (self.windowContainer.children[i].asset.id === 'shop_upgrade_bg' || self.windowContainer.children[i].asset.id === 'shop_upgrade_bg_selected')) {
			background = self.windowContainer.children[i];
			break;
		}
	}
	if (background) {
		background.asset = isSelected ? 'shop_upgrade_bg_selected' : 'shop_upgrade_bg';
	}
};
var SaveManager = {
	// Save all game data
	save: function save() {
		// Update gold in registry from score manager
		UPGRADE_REGISTRY.gold = Number(scoreManager.getScore()) || 0;
		// Save gold from registry to storage
		storage.gold = UPGRADE_REGISTRY.gold;
		// Save all upgrade levels directly from registry
		storage.healthUpgrade = UPGRADE_REGISTRY.health_increase;
		storage.arrowUpgrade = UPGRADE_REGISTRY.arrow_capacity;
		storage.levitateUpgrade = UPGRADE_REGISTRY.levitate;
		storage.slideUpgrade = UPGRADE_REGISTRY.slide_damage;
		storage.lootUpgrade = UPGRADE_REGISTRY.better_loot;
		storage.potionChanceUpgrade = UPGRADE_REGISTRY.potion_chance;
		storage.arrowFinderUpgrade = UPGRADE_REGISTRY.arrow_finder;
		storage.groundSlamUpgrade = UPGRADE_REGISTRY.ground_slam;
		storage.lootMagnetUpgrade = UPGRADE_REGISTRY.loot_magnet;
		// Save tracking_arrow upgrade
		storage.trackingArrowUpgrade = UPGRADE_REGISTRY.tracking_arrow;
		// Track timestamp
		storage.lastSaveTime = Date.now();
	},
	// Load saved game data
	load: function load() {
		// Initialize registry from storage with safe number conversions
		UPGRADE_REGISTRY.health_increase = parseInt(storage.healthUpgrade) || 0;
		UPGRADE_REGISTRY.arrow_capacity = parseInt(storage.arrowUpgrade) || 0;
		UPGRADE_REGISTRY.levitate = parseInt(storage.levitateUpgrade) || 0;
		UPGRADE_REGISTRY.slide_damage = parseInt(storage.slideUpgrade) || 0;
		UPGRADE_REGISTRY.better_loot = parseInt(storage.lootUpgrade) || 0;
		UPGRADE_REGISTRY.potion_chance = parseInt(storage.potionChanceUpgrade) || 0;
		UPGRADE_REGISTRY.arrow_finder = parseInt(storage.arrowFinderUpgrade) || 0;
		UPGRADE_REGISTRY.ground_slam = parseInt(storage.groundSlamUpgrade) || 0;
		UPGRADE_REGISTRY.loot_magnet = parseInt(storage.lootMagnetUpgrade) || 0;
		// Load tracking_arrow upgrade
		UPGRADE_REGISTRY.tracking_arrow = parseInt(storage.trackingArrowUpgrade) || 0;
		// Load gold with explicit number conversion
		UPGRADE_REGISTRY.gold = parseInt(storage.gold) || 0;
		// Update score display
		if (scoreManager) {
			scoreManager.setScore(UPGRADE_REGISTRY.gold);
		}
		// If shop exists, update the visual state of upgrades
		this.updateShopDisplay();
		// Apply the loaded upgrade effects to the player
		this.applyUpgradeEffects();
	},
	// Update shop display based on registry values
	updateShopDisplay: function updateShopDisplay() {
		if (game.shopManager && game.shopManager.upgrades) {
			game.shopManager.upgrades.forEach(function (upgrade) {
				// Get the appropriate level from registry
				var registryLevel = 0;
				switch (upgrade.id) {
					case 'health_increase':
						registryLevel = UPGRADE_REGISTRY.health_increase;
						break;
					case 'arrow_capacity':
						registryLevel = UPGRADE_REGISTRY.arrow_capacity;
						break;
					case 'levitate':
						registryLevel = UPGRADE_REGISTRY.levitate;
						break;
					case 'slide_damage':
						registryLevel = UPGRADE_REGISTRY.slide_damage;
						break;
					case 'better_loot':
						registryLevel = UPGRADE_REGISTRY.better_loot;
						break;
					case 'potion_chance':
						registryLevel = UPGRADE_REGISTRY.potion_chance;
						break;
					case 'arrow_finder':
						registryLevel = UPGRADE_REGISTRY.arrow_finder;
						break;
					case 'ground_slam':
						registryLevel = UPGRADE_REGISTRY.ground_slam;
						break;
					case 'loot_magnet':
						registryLevel = UPGRADE_REGISTRY.loot_magnet;
						break;
					case 'tracking_arrow':
						registryLevel = UPGRADE_REGISTRY.tracking_arrow;
						break;
				}
				// Update internal level directly from registry
				upgrade.level = registryLevel;
				// Update UI text
				if (upgrade.levelText) {
					upgrade.levelText.setText('LEVEL ' + upgrade.level + (upgrade.level >= upgrade.maxLevel ? ' MAX' : ''));
				}
				console.log("Updated " + upgrade.id + " display to level " + upgrade.level);
			});
		}
	},
	// Apply upgrade effects to player
	applyUpgradeEffects: function applyUpgradeEffects(player) {
		if (!player && window.player) {
			player = window.player;
		}
		if (!player) {
			return;
		}
		// Reset to base values
		player.maxHealth = 3;
		player.ammoCount = 5;
		player.hasLevitate = false;
		player.slideDamage = false;
		player.hasGroundSlam = false;
		player.arrowDropChance = 1.0;
		player.lootMultiplier = 1.0;
		player.potionDropChance = 1.0;
		player.lootMagnetRange = 1.0;
		// Apply all upgrades from registry
		player.maxHealth += UPGRADE_REGISTRY.health_increase;
		player.currentHealth = player.maxHealth;
		player.ammoCount += UPGRADE_REGISTRY.arrow_capacity * 2;
		player.hasLevitate = UPGRADE_REGISTRY.levitate > 0;
		player.hasGroundSlam = UPGRADE_REGISTRY.ground_slam > 0;
		player.slideDamage = UPGRADE_REGISTRY.slide_damage > 0;
		player.lootMultiplier = 1 + UPGRADE_REGISTRY.better_loot * 0.15;
		player.potionDropChance = 1 + UPGRADE_REGISTRY.potion_chance * 0.5;
		player.arrowDropChance = 1 + UPGRADE_REGISTRY.arrow_finder * 0.5;
		player.lootMagnetRange = 1 + UPGRADE_REGISTRY.loot_magnet * 0.5;
		// Update UI elements
		if (player.heartContainer) {
			player.heartContainer.updateMaxHealth(player.maxHealth);
			player.heartContainer.updateHealth(player.currentHealth);
		}
		if (scoreManager) {
			scoreManager.updateAmmo(player.ammoCount);
		}
	},
	// Clear all saved data
	clear: function clear() {
		// Reset storage
		storage.gold = 0;
		storage.healthUpgrade = 0;
		storage.arrowUpgrade = 0;
		storage.levitateUpgrade = 0;
		storage.slideUpgrade = 0;
		storage.lootUpgrade = 0;
		// Reset registry
		UPGRADE_REGISTRY.gold = 0;
		UPGRADE_REGISTRY.health_increase = 0;
		UPGRADE_REGISTRY.arrow_capacity = 0;
		UPGRADE_REGISTRY.levitate = 0;
		UPGRADE_REGISTRY.slide_damage = 0;
		UPGRADE_REGISTRY.better_loot = 0;
		UPGRADE_REGISTRY.potion_chance = 0;
		UPGRADE_REGISTRY.arrow_finder = 0;
	}
};
ShopManager.prototype.initPlayerAnimation = function () {
	var self = this;
	self.playerSprites = [];
	self.idleFrame = 0;
	self.animationCounter = 0;
	self.animationSpeed = 0.037;
	// Create sprites for idle animation
	for (var i = 1; i <= 4; i++) {
		// Create and attach first
		var sprite = self.attachAsset('playeridle' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			x: -550,
			y: 400
		});
		// Set initial visibility
		sprite.alpha = i === 1 ? 1 : 0;
		// Push to array after attaching
		self.playerSprites.push(sprite);
	}
};
ShopManager.prototype.hideAllSprites = function () {
	var self = this;
	for (var i = 0; i < self.playerSprites.length; i++) {
		self.playerSprites[i].alpha = 0;
	}
};
ShopManager.prototype.updatePlayerAnimation = function () {
	var self = this;
	// Hide all sprites first
	self.hideAllSprites();
	// Update animation
	self.animationCounter += self.animationSpeed;
	if (self.animationCounter >= 1) {
		self.animationCounter = 0;
		self.idleFrame = (self.idleFrame + 1) % self.playerSprites.length;
	}
	// Show current frame
	self.playerSprites[self.idleFrame].alpha = 1;
};
ShopManager.prototype.updateArrowVisibility = function () {
	var self = this;
	// Check if arrows exist before updating their visibility
	if (self.leftArrow) {
		// Only show left arrow if we're not at the first upgrade
		self.leftArrow.alpha = self.currentUpgradeIndex > 0 ? 1 : 0;
	}
	if (self.rightArrow) {
		// Only show right arrow if we're not at the last upgrade
		self.rightArrow.alpha = self.currentUpgradeIndex < self.categoryUpgrades.length - 1 ? 1 : 0;
	}
};
ShopManager.prototype.createTabs = function () {
	var self = this;
	// Add shop background first
	self.shopBackground = self.attachAsset('shop_background', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add shop banner
	self.shopBanner = self.attachAsset('shop_banner', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -1000
	});
	// Create tabs container AFTER background
	self.tabs = new Container();
	self.tabs.x = 0;
	self.tabs.y = -880;
	self.addChild(self.tabs); // This adds it on top of previously added elements
	// Add windows
	self.playerWindow = self.attachAsset('shop_player', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: -490,
		y: 60
	});
	// Gold display
	self.goldWindow = new Container(); // Create container instead of asset
	// Add dark window background first
	var goldWindowBg = self.goldWindow.attachAsset('shop_dark_window', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.goldWindow.x = -490;
	self.goldWindow.y = 850;
	self.addChild(self.goldWindow);
	self.createGoldDisplay();
	// Create category tabs
	var spacing = 335;
	var startX = -(self.categories.length - 1) * spacing / 2;
	self.categories.forEach(function (category, index) {
		var tab = new Container();
		// Create both tab backgrounds
		var normalBg = tab.attachAsset('shop_tab', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		var selectedBg = tab.attachAsset('shop_tab_selected', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		selectedBg.alpha = 0; // Initially hide the selected state
		var text = new CustomText(category, {
			size: 48,
			fill: 0xE6D5AC,
			letterSpacing: 5
		});
		tab.addChild(text);
		// Position text at center of tab
		text.x = -text.width / 2 + 10;
		text.y = 0;
		tab.x = startX + spacing * index;
		tab.y = 180;
		tab.down = function () {
			// Play sound when tab is clicked
			LK.getSound('menuselect').play();
			self.showCategory(category);
		};
		self.tabs.addChild(tab);
	});
};
ShopManager.prototype.createBuyButton = function () {
	var self = this;
	self.buyButton = new Container();
	self.buyButtonBg = self.buyButton.attachAsset('shop_button', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.priceContainer = new Container();
	self.priceText = new CustomText('', {
		size: 60,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	self.priceCoinIcon = LK.getAsset('coin', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	// Position coin at fixed location
	self.priceCoinIcon.x = 160;
	self.priceCoinIcon.y = 40;
	// Initially position text with some default value
	self.priceText.x = self.priceCoinIcon.x - 10;
	self.priceText.y = 40;
	self.priceContainer.addChild(self.priceText);
	self.priceContainer.addChild(self.priceCoinIcon);
	self.priceContainer.y = -300;
	self.buyButton.addChild(self.priceContainer);
	self.buyButton.x = 525;
	self.buyButton.y = 850;
	self.buyButton.down = function () {
		self.purchaseSelectedUpgrade();
	};
	self.addChild(self.buyButton);
};
ShopManager.prototype.showCategory = function (category) {
	var self = this;
	self.currentCategory = category;
	// Filter upgrades for this category
	self.categoryUpgrades = self.upgrades.filter(function (upgrade) {
		return upgrade.category === category;
	});
	// Reset to first upgrade in category
	self.currentUpgradeIndex = 0;
	// Show first upgrade
	self.showUpgradeAtIndex(0);
	// Update tab visuals
	self.tabs.children.forEach(function (tab) {
		var isSelected = tab.children[2].text === category;
		tab.children[0].alpha = isSelected ? 0 : 1; // normal bg
		tab.children[1].alpha = isSelected ? 1 : 0; // selected bg
	});
};
ShopManager.prototype.showUpgradeAtIndex = function (index) {
	var self = this;
	// Update current index
	self.currentUpgradeIndex = index;
	// Clear existing upgrades
	self.upgradeList.removeChildren();
	// Show upgrade at current index
	if (self.categoryUpgrades.length > 0) {
		var upgradeConfig = self.categoryUpgrades[self.currentUpgradeIndex];
		var upgrade = new ShopUpgrade(upgradeConfig);
		// Initialize level from registry based on ID
		switch (upgrade.id) {
			case 'health_increase':
				upgrade.level = UPGRADE_REGISTRY.health_increase;
				break;
			case 'arrow_capacity':
				upgrade.level = UPGRADE_REGISTRY.arrow_capacity;
				break;
			case 'levitate':
				upgrade.level = UPGRADE_REGISTRY.levitate;
				break;
			case 'slide_damage':
				upgrade.level = UPGRADE_REGISTRY.slide_damage;
				break;
			case 'better_loot':
				upgrade.level = UPGRADE_REGISTRY.better_loot;
				break;
			case 'potion_chance':
				upgrade.level = UPGRADE_REGISTRY.potion_chance || 0;
				break;
			case 'arrow_finder':
				upgrade.level = UPGRADE_REGISTRY.arrow_finder || 0;
				break;
			case 'ground_slam':
				upgrade.level = UPGRADE_REGISTRY.ground_slam;
				break;
			case 'loot_magnet':
				upgrade.level = UPGRADE_REGISTRY.loot_magnet;
				break;
			case 'tracking_arrow':
				upgrade.level = UPGRADE_REGISTRY.tracking_arrow || 0;
				break;
		}
		// Update level text to match registry
		upgrade.levelText.setText('LEVEL ' + upgrade.level + (upgrade.level >= upgrade.maxLevel ? ' MAX' : ''));
		upgrade.x = 355;
		upgrade.y = 60;
		self.upgradeList.addChild(upgrade);
		// Make sure we're selecting the upgrade
		self.selectedUpgrade = upgrade;
	}
	// Update arrow visibility
	self.updateArrowVisibility();
	// Update buy button
	self.updateBuyButton();
};
ShopManager.prototype.createArrows = function () {
	var self = this;
	// Create left arrow
	self.leftArrow = new Container();
	self.leftArrow.attachAsset('shop_arrow', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: -1,
		// Flip horizontally
		alpha: 1
	});
	self.leftArrow.x = 0;
	self.leftArrow.y = 150; // Center with the upgrade display
	self.addChild(self.leftArrow);
	// Create right arrow
	self.rightArrow = new Container();
	self.rightArrow.attachAsset('shop_arrow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 1
	});
	self.rightArrow.x = 710;
	self.rightArrow.y = 150; // Center with the upgrade display
	self.addChild(self.rightArrow);
	// Set up interactions
	self.leftArrow.down = function () {
		if (self.currentUpgradeIndex > 0) {
			LK.getSound('menuselect').play();
			self.showUpgradeAtIndex(self.currentUpgradeIndex - 1);
		}
	};
	self.rightArrow.down = function () {
		if (self.currentUpgradeIndex < self.categoryUpgrades.length - 1) {
			LK.getSound('menuselect').play();
			self.showUpgradeAtIndex(self.currentUpgradeIndex + 1);
		}
	};
	// Set initial visibility
	if (self.leftArrow && self.rightArrow) {
		self.updateArrowVisibility();
	}
};
ShopManager.prototype.selectUpgrade = function (upgrade) {
	var self = this;
	// Deselect previous upgrade
	if (self.selectedUpgrade) {
		self.selectedUpgrade.setSelected(false);
	}
	self.selectedUpgrade = upgrade;
	upgrade.setSelected(true);
	// Update buy button and price
	self.updateBuyButton();
};
ShopManager.prototype.updateBuyButton = function () {
	var self = this;
	if (!self.selectedUpgrade) {
		return;
	}
	var price = self.selectedUpgrade.getCurrentPrice();
	var canAfford = scoreManager.getScore() >= price;
	var isMaxLevel = self.selectedUpgrade.level >= self.selectedUpgrade.maxLevel;
	// Store current price length to detect changes
	var oldLength = self.priceText.text.length;
	var newLength = (isMaxLevel ? 'MAX' : price.toString()).length;
	// Store current vertical position
	var currentY = self.priceText.y;
	// Update buy button based on max level first
	if (isMaxLevel) {
		self.priceText.setText('MAX');
		self.priceCoinIcon.visible = false;
		self.priceText.x = 0; // Center the MAX text
		self.buyButtonBg.tint = 0x888888;
	} else {
		// If changing from 4 to 3 digits, completely recreate the text
		if (oldLength > 3 && newLength === 3) {
			// Remove the old text
			if (self.priceText.parent) {
				self.priceContainer.removeChild(self.priceText);
			}
			// Create new text with same properties
			self.priceText = new CustomText(price.toString(), {
				size: 60,
				fill: 0xFFFFFF,
				letterSpacing: 5
			});
			// Restore the vertical position
			self.priceText.y = currentY;
			self.priceContainer.addChild(self.priceText);
		} else {
			// Just update the text for other cases
			self.priceText.setText(price.toString());
		}
		self.priceCoinIcon.visible = true;
		self.priceCoinIcon.x = 160; // Fixed position
		var textPadding = 10;
		self.priceText.x = self.priceCoinIcon.x - textPadding - self.priceText.width;
		self.buyButtonBg.tint = canAfford ? 0xFFFFFF : 0x888888;
	}
	// Update total gold display
	self.updateTotalGold();
};
ShopManager.prototype.purchaseSelectedUpgrade = function () {
	var self = this;
	if (!self.selectedUpgrade) {
		return;
	}
	var price = self.selectedUpgrade.getCurrentPrice();
	if (scoreManager.getScore() >= price && self.selectedUpgrade.level < self.selectedUpgrade.maxLevel) {
		// Add visual feedback animation for buy button
		tween(self.buyButton, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(self.buyButton, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut
				});
			}
		});
		// Deduct price using the global scoreManager
		scoreManager.addScore(-price);
		// Call purchase() method
		var result = self.selectedUpgrade.purchase();
		// Play purchase sound
		LK.getSound('shopbuy').play();
		// Update button UI
		self.updateBuyButton();
		// Save the changes
		SaveManager.save();
	} else {
		// Play "can't buy" sound when player doesn't have enough gold or upgrade is maxed
		LK.getSound('cantbuy').play();
	}
};
ShopManager.prototype.createGoldDisplay = function () {
	var self = this;
	// Create a container for the gold display
	self.goldDisplayContainer = new Container();
	self.totalGoldText = new CustomText('0', {
		size: 90,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.totalGoldCoinIcon = LK.getAsset('coin', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	self.goldDisplayContainer.addChild(self.totalGoldText);
	self.goldDisplayContainer.addChild(self.totalGoldCoinIcon);
	self.goldDisplayContainer.x = 90; // Center of gold window
	self.goldDisplayContainer.y = -50;
	// Position text and coin
	self.totalGoldText.x = -50;
	self.totalGoldText.y = 65;
	self.totalGoldCoinIcon.x = 70;
	self.totalGoldCoinIcon.y = 70;
	// Add to the window
	self.goldWindow.addChild(self.goldDisplayContainer);
	// Initial update
	self.updateTotalGold();
};
ShopManager.prototype.updateTotalGold = function () {
	var self = this;
	var totalGold = scoreManager.getScore();
	self.totalGoldText.setText(totalGold.toString());
	// Keep coin at fixed position
	self.totalGoldCoinIcon.x = 165; // Fixed position
	// Right-align text relative to coin
	// Adjust position based on digit count but keep right edge aligned
	var textPadding = 50; // Space between text and coin
	self.totalGoldText.x = self.totalGoldCoinIcon.x - textPadding - self.totalGoldText.width;
};
ShopManager.prototype.update = function () {
	this.updatePlayerAnimation();
	this.updateBuyButton();
};
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;
		}
		// Only apply magnet effect if it's not a health potion OR if player is not at full health
		if (!(self instanceof HealthPotion && player.currentHealth >= player.maxHealth)) {
			if (player.lootMagnetRange > 1) {
				var dx = player.x - self.x;
				var dy = player.y - self.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				var maxAttractionRange = 300 * player.lootMagnetRange;
				if (distance < maxAttractionRange) {
					// Calculate attraction strength based on distance
					var strength = (1 - distance / maxAttractionRange) * 3;
					self.velocityX += dx / distance * strength;
					self.velocityY += dy / distance * strength;
				}
			}
		}
		// 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 magnetRange = player.lootMagnetRange || 1;
		var itemBounds = {
			left: self.x - 25 * magnetRange,
			right: self.x + 25 * magnetRange,
			top: self.y - 25 * magnetRange,
			bottom: self.y + 25 * magnetRange
		};
		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;
var RICOCHET_SEARCH_RADIUS_SQ = 1000 * 1000; // Further increased search radius for ricochet targets
var RICOCHET_ARROW_SPEED = 40;
var UPGRADE_REGISTRY = {
	gold: 0,
	health_increase: 0,
	arrow_capacity: 0,
	levitate: 0,
	slide_damage: 0,
	better_loot: 0,
	potion_chance: 0,
	arrow_finder: 0,
	ground_slam: 0,
	loot_magnet: 0,
	tracking_arrow: 0
};
/**** 
* Utilities
****/ 
function initializeStorage() {
	if (storage.gold === undefined) {
		storage.gold = 0;
	}
	if (storage.healthUpgrade === undefined) {
		storage.healthUpgrade = 0;
	}
	if (storage.arrowUpgrade === undefined) {
		storage.arrowUpgrade = 0;
	}
	if (storage.levitateUpgrade === undefined) {
		storage.levitateUpgrade = 0;
	}
	if (storage.slideUpgrade === undefined) {
		storage.slideUpgrade = 0;
	}
	if (storage.lootUpgrade === undefined) {
		storage.lootUpgrade = 0;
	}
	if (storage.potionChanceUpgrade === undefined) {
		storage.potionChanceUpgrade = 0;
	}
	if (storage.arrowFinderUpgrade === undefined) {
		storage.arrowFinderUpgrade = 0;
	}
	if (storage.groundSlamUpgrade === undefined) {
		storage.groundSlamUpgrade = 0;
	}
	if (storage.lootMagnetUpgrade === undefined) {
		storage.lootMagnetUpgrade = 0;
	}
	// Add trackingArrowUpgrade initialization
	if (storage.trackingArrowUpgrade === undefined) {
		storage.trackingArrowUpgrade = 0;
	}
}
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 first
	self.score = 0;
	// Define methods before using them
	self.getScore = function () {
		return self.score;
	};
	self.setScore = function (newScore) {
		var oldScore = self.score;
		self.score = newScore;
		// Update the registry
		UPGRADE_REGISTRY.gold = self.score;
		// Check if digit count has changed
		var oldLength = oldScore.toString().length;
		var newLength = newScore.toString().length;
		if (oldLength !== newLength && self.scoreText) {
			// Store current position
			var currentX = self.scoreText.x;
			var currentY = self.scoreText.y;
			// Remove old text
			if (self.scoreText.parent) {
				self.container.removeChild(self.scoreText);
			}
			// Create new text
			self.scoreText = new CustomText(newScore.toString(), {
				size: 100,
				fill: 0xFFFFFF,
				letterSpacing: 5
			});
			// Restore position properties
			self.scoreText.y = currentY;
			self.container.addChild(self.scoreText);
		} else if (self.scoreText) {
			// Just update text content
			self.scoreText.setText(newScore.toString());
		}
		// Keep right edge at a fixed position
		if (self.scoreText) {
			var rightEdgePosition = -10; // Fixed right edge position
			self.scoreText.x = rightEdgePosition - self.scoreText.width;
		}
	};
	self.addScore = function (amount, x, y) {
		self.setScore(self.score + amount);
		// Update the registry
		UPGRADE_REGISTRY.gold = self.score;
		SaveManager.save();
		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);
		}
	};
	// Create UI elements
	self.scoreText = new CustomText('0', {
		size: 100,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	// Initialize with default position
	self.scoreText.x = -100;
	// AFTER UI elements exist AND methods are defined, load saved score
	if (storage.gold) {
		self.score = Number(storage.gold);
		self.setScore(self.score); // Now this is safe because scoreText exists and setScore is defined
	}
	self.coinIcon = LK.getAsset('coin', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// New ammo display
	self.ammoText = new CustomText('0*', {
		size: 90,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	self.ammoIcon = LK.getAsset('arrow', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3,
		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
	// Position elements
	self.scoreText.x = -200; // Less negative value
	self.scoreText.y = 0; // Center vertically
	self.coinIcon.x = 0;
	self.coinIcon.y = 0;
	self.ammoText.x = 30;
	self.ammoText.y = 170; // Place below coin display
	self.ammoIcon.x = 55;
	self.ammoIcon.y = 240; // Place below coin display
	// Add ammo update method
	self.updateAmmo = function (amount) {
		self.ammoText.setText(amount + '*');
		// Instead of hardcoded adjustments based on digit count,
		// calculate the width of the text and properly align it
		// Keep the right edge at a fixed position
		var rightEdgePosition = 30; // Fixed right edge position
		self.ammoText.x = rightEdgePosition - self.ammoText.width;
	};
	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 RICOCHET_SEARCH_RADIUS_SQ = 500 * 500; // Moved to Constants section
// var RICOCHET_ARROW_SPEED = 40; // Moved to Constants section
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 arrowParticles = []; // Array to hold arrow trail particles
var jarSpawnCounter = 0;
var jarSpawnInterval = 10;
var chestSpawnCounter = 0;
var chestSpawnInterval = 75;
var enemies = [];
var enemySpawnCounter = 0;
var enemySpawnInterval = 100;
var eyeballSpawnCounter = 0;
var eyeballSpawnInterval = 200;
var particleSystem;
var heartContainer = new HeartContainer();
scoreManager = new ScoreManager(); // Create it early
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,
	tint: 0x777777
}));
var mg2 = midgroundContainer.addChild(LK.getAsset('midground', {
	anchorX: 0,
	anchorY: 1,
	tint: 0x777777
}));
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,
	tint: 0x777777
}));
var fg2 = foregroundContainer.addChild(LK.getAsset('foreground', {
	anchorX: 0,
	anchorY: 1,
	tint: 0x777777
}));
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);
}
function createGameplayShopButton() {
	// Create shop button in top corner
	var shopButton = game.addChild(LK.getAsset('shopbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.7,
		scaleY: 0.7
	}));
	shopButton.x = 250;
	shopButton.y = 310; // Position below score display
	shopButton.down = function () {
		// Pause the game
		var previousGameSpeed = gameSpeedMultiplier;
		// IMPORTANT FIX: Force cancel any active movement states
		if (player) {
			// Cancel slide if active
			if (player.isSliding) {
				player.isSliding = false;
				player.slideTimer = 0;
				player.hitboxHeight = player.normalHitboxHeight;
			}
			// Cancel air dash if active
			if (player.isAirDashing) {
				player.isAirDashing = false;
				player.airDashTimer = 0;
				// Clean up shadows
				player.shadowImages.forEach(function (shadow) {
					if (shadow.parent) {
						shadow.destroy();
					}
				});
				player.shadowImages = [];
				// Reset rotation
				player.rotation = 0;
			}
		}
		// Now set game speed to 0
		gameSpeedMultiplier = 0;
		// Open shop
		openGameplayShop();
		// Function to return to game
		function resumeGame() {
			// Close shop
			if (game.shopManager && game.shopManager.parent) {
				game.shopManager.destroy();
				game.shopManager = null;
			}
			// Restore game speed, but ensure it's not the enhanced speed from slide/dash
			gameSpeedMultiplier = 1.0; // IMPORTANT FIX: Always restore to 1.0, not previousGameSpeed
			// Remove back button using our reference
			if (gameplayBackButton && gameplayBackButton.parent) {
				gameplayBackButton.destroy();
			}
		}
		// Add custom back button for gameplay shop
		gameplayBackButton = game.addChild(LK.getAsset('backbutton', {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 0.75,
			scaleY: 0.75,
			x: GAME_WIDTH / 2,
			y: GAME_HEIGHT - 200
		}));
		gameplayBackButton.down = function () {
			LK.getSound('menuselect').play();
			resumeGame();
		};
	};
	return shopButton;
}
// Modified openShop function for gameplay
function openGameplayShop() {
	SaveManager.load();
	// Create shop if it doesn't exist
	if (!game.shopManager) {
		game.shopManager = new ShopManager();
		game.shopManager.x = GAME_WIDTH / 2;
		game.shopManager.y = GAME_HEIGHT / 2;
	}
	// Show shop and force an update
	game.addChild(game.shopManager);
	game.shopManager.updateTotalGold();
	// Override the purchase function to apply upgrades immediately
	var originalPurchase = game.shopManager.purchaseSelectedUpgrade;
	game.shopManager.purchaseSelectedUpgrade = function () {
		originalPurchase.call(game.shopManager);
		SaveManager.applyUpgradeEffects(player);
	};
}
function applyPlayerUpgradeEffects() {
	if (!player) {
		return;
	}
	// Apply health upgrade if saved
	if (storage.playerMaxHealth) {
		player.maxHealth = parseInt(storage.playerMaxHealth);
		player.currentHealth = player.maxHealth;
		player.heartContainer.updateHealth(player.currentHealth);
	}
	// Apply arrow capacity upgrade if saved
	if (storage.playerArrowCapacity) {
		player.ammoCount = parseInt(storage.playerArrowCapacity);
		scoreManager.updateAmmo(player.ammoCount);
	}
	// Apply levitate upgrade if saved
	if (storage.playerHasLevitate) {
		player.hasLevitate = true;
	}
	// Apply any other player effect upgrades here
}
// 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 / 3;
	tutorialButton.y = GAME_HEIGHT / 1.15;
	tween(tutorialButton, {
		alpha: 1
	}, {
		duration: 1000,
		easing: tween.easeIn
	});
	var shopButton = titleScreen.attachAsset('shopbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0
	});
	shopButton.x = GAME_WIDTH / 1.4;
	shopButton.y = GAME_HEIGHT / 1.15; // Position between play and tutorial buttons
	shopButton.down = function () {
		// Add visual feedback animation
		tween(shopButton, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(shopButton, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						openShop();
					}
				});
			}
		});
		// Play menu select sound
		LK.getSound('menuselect').play();
	};
	tween(shopButton, {
		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();
}
function resetAllStorage() {
	// Log the current state
	// Delete all stored values
	storage.gold = 0;
	storage.healthUpgrade = 0;
	storage.arrowUpgrade = 0;
	storage.levitateUpgrade = 0;
	storage.slideUpgrade = 0;
	storage.lootUpgrade = 0;
	storage.groundSlamUpgrade = 0;
	storage.potionChanceUpgrade = 0;
	storage.arrowFinderUpgrade = 0;
}
function openShop() {
	// Hide title screen
	titleScreen.visible = false;
	SaveManager.load();
	// Create shop if it doesn't exist
	if (!game.shopManager) {
		game.shopManager = new ShopManager();
		game.shopManager.x = GAME_WIDTH / 2;
		game.shopManager.y = GAME_HEIGHT / 2;
	}
	// Show shop and force an update
	game.addChild(game.shopManager);
	game.shopManager.updateTotalGold();
	// Add back button
	var backButton = game.shopManager.attachAsset('backbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 0,
		y: 1100
	});
	backButton.down = function () {
		// Add visual feedback animation
		tween(backButton, {
			scaleX: 0.65,
			scaleY: 0.65
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(backButton, {
					scaleX: 0.75,
					scaleY: 0.75
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						closeShop();
					}
				});
			}
		});
		// Play menu select sound
		LK.getSound('menuselect').play();
	};
}
function closeShop() {
	// Save the current state before closing
	SaveManager.save();
	if (game.shopManager && game.shopManager.parent) {
		game.shopManager.destroy(); // Destroy the manager and all its children
		game.shopManager = null; // Clear the reference
	}
	// Ensure title screen is visible after shop closes
	if (titleScreen && !titleScreen.parent) {
		// If title screen was destroyed or removed, recreate it
		createTitleScreen();
	} else if (titleScreen) {
		titleScreen.visible = true;
	}
}
// 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
	SaveManager.load();
	player = game.addChild(new Player());
	player.x = GAME_WIDTH / 4.5;
	player.y = GAME_HEIGHT / 1.5 - 100;
	game.addChild(heartContainer);
	SaveManager.applyUpgradeEffects(player);
	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
	game.addChild(scoreManager.container);
	initializeGame();
	// Initialize particle system
	particleSystem = new ParticlePool(100);
	game.addChild(particleSystem);
	// Add shop button to gameplay
	createGameplayShopButton();
	// Show health
	player.heartContainer.alpha = 1;
	player.heartVisibilityTimer = 120;
	// Play background music
	LK.playMusic('backgroundmusic1', {
		fade: {
			start: 0,
			end: 0.7,
			duration: 3500
		}
	});
}
// 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;
		// Calculate trap platform chance based on time
		var baseTrapChance = 0.01; // 1% base chance
		var additionalChance = Math.min(0.15, timeSinceStart / 300000); // Up to 15% extra after 5 minutes
		var totalTrapChance = baseTrapChance + additionalChance;
		if (currentPlatformPattern === 'C') {
			// Single platform for straight sections, using stored height
			var platform;
			if (Math.random() < totalTrapChance) {
				platform = new CrumblePlatform();
			} else {
				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, platformLower;
			// Create upper platform
			if (Math.random() < totalTrapChance) {
				platformUpper = new CrumblePlatform();
			} else {
				platformUpper = new Platform();
			}
			// Create lower platform
			if (Math.random() < totalTrapChance) {
				platformLower = new CrumblePlatform();
			} else {
				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 && !(p instanceof CrumblePlatform);
		});
		// 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 && !(p instanceof CrumblePlatform);
		});
		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 (game.shopManager && game.shopManager.parent) {
		return;
	}
	if (!tutorialActive) {
		var isPlatformOccupied = function isPlatformOccupied(platform) {
			return enemies.some(function (enemy) {
				return enemy.currentPlatform === platform && (enemy.type === 'goblin' || enemy.type === 'skeleton' || enemy.type === 'mushroom');
			});
		};
		// Get available platforms for ground enemies
		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);
		});
		// Unified enemy spawn counter for all ground enemies
		enemySpawnCounter++;
		// If we have available platforms and it's time to spawn
		if (enemySpawnCounter >= enemySpawnInterval && unoccupiedPlatforms.length > 0) {
			// Decide how many enemies to spawn (1-3 based on available platforms)
			var spawnCount = Math.min(Math.floor(Math.random() * 3) + 1, unoccupiedPlatforms.length);
			for (var i = 0; i < spawnCount; i++) {
				// Choose a random platform from unoccupied ones
				var platformIndex = Math.floor(Math.random() * unoccupiedPlatforms.length);
				var platform = unoccupiedPlatforms[platformIndex];
				// Choose enemy type randomly with weighted probabilities
				var enemyRandom = Math.random();
				var enemyType;
				if (enemyRandom < 0.45) {
					enemyType = 'goblin'; // 45% chance for goblin
				} else if (enemyRandom < 0.75) {
					enemyType = 'mushroom'; // 30% chance for mushroom
				} else {
					enemyType = 'skeleton'; // 25% chance for skeleton
				}
				// Create the enemy
				var enemy = new Enemy(enemyType);
				enemy.x = platform.x;
				enemy.y = platform.y - ENEMY_PLATFORM_OFFSET;
				enemy.currentPlatform = platform;
				enemies.push(enemy);
				game.addChild(enemy);
				// Remove the used platform from available platforms
				unoccupiedPlatforms.splice(platformIndex, 1);
			}
			// Reset the counter with a random interval
			enemySpawnInterval = Math.floor(Math.random() * 100) + 150;
			enemySpawnCounter = 0;
		}
		// Eyeball spawning - kept separate since they don't use platforms
		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;
			enemies.push(enemy);
			game.addChild(enemy);
			// Random interval between 200-400 ticks
			eyeballSpawnInterval = Math.floor(Math.random() * 200) + 200;
			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();
					}
					// Ricochet logic for tracking arrows
					if (UPGRADE_REGISTRY.tracking_arrow > 0) {
						var firstHitEnemy = enemies[j]; // The enemy that was just hit by 'arrow'
						var closestSecondEnemy = null;
						var minDistanceSqToSecondEnemy = RICOCHET_SEARCH_RADIUS_SQ;
						for (var k = 0; k < enemies.length; k++) {
							var potentialSecondEnemy = enemies[k];
							// Ensure we're not targeting the same enemy, and the potential target is valid and in front of the player
							if (potentialSecondEnemy === firstHitEnemy || !potentialSecondEnemy || potentialSecondEnemy.isDying || potentialSecondEnemy.isHit || potentialSecondEnemy.destroyed || player && potentialSecondEnemy.x < player.x) {
								// Check against player's X position
								continue;
							}
							var dxRicochet = potentialSecondEnemy.x - firstHitEnemy.x;
							var dyRicochet = potentialSecondEnemy.y - firstHitEnemy.y;
							var distSqRicochet = dxRicochet * dxRicochet + dyRicochet * dyRicochet;
							if (distSqRicochet < minDistanceSqToSecondEnemy) {
								minDistanceSqToSecondEnemy = distSqRicochet;
								closestSecondEnemy = potentialSecondEnemy;
							}
						}
						if (closestSecondEnemy) {
							var ricochetArrow = new Arrow();
							ricochetArrow.x = firstHitEnemy.x;
							ricochetArrow.y = firstHitEnemy.y;
							// Set startX/Y for tracking logic, relative to the new arrow's origin
							ricochetArrow.startX = ricochetArrow.x;
							ricochetArrow.startY = ricochetArrow.y;
							// Calculate the angle to the target and set it as the arrow's rotation
							ricochetArrow.rotation = Math.atan2(closestSecondEnemy.y - ricochetArrow.y, closestSecondEnemy.x - ricochetArrow.x);
							// Set the sprite rotation to match immediately
							ricochetArrow.sprite.rotation = ricochetArrow.rotation;
							// IMPORTANT: Update the velocity components to match the rotation
							ricochetArrow.velocityX = Math.cos(ricochetArrow.rotation) * RICOCHET_ARROW_SPEED;
							ricochetArrow.velocityY = Math.sin(ricochetArrow.rotation) * RICOCHET_ARROW_SPEED;
							ricochetArrow.speed = RICOCHET_ARROW_SPEED;
							ricochetArrow.damage = arrow.damage;
							game.addChild(ricochetArrow);
							arrows.push(ricochetArrow);
						}
					}
					// 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);
			}
		}
		// In the updateEnemies function where spore collisions are checked
		if (window.spores) {
			for (var i = window.spores.length - 1; i >= 0; i--) {
				var spore = window.spores[i];
				// Skip if spore is invalid or already marked for removal
				if (!spore || !spore.parent || spore.destroyed) {
					window.spores.splice(i, 1);
					continue;
				}
				// Update spore
				spore.update();
				// Only check collisions if the spore is active and player can take damage
				if (spore.isActive && !player.isInvulnerable && !player.isDying) {
					if (GameUtils.checkCollision(spore.getBounds(), player.getCollisionBounds())) {
						player.takeDamage();
						spore.hasHitPlayer = true;
						spore.explode();
					}
				}
				// Safety check - if spore is destroyed but still in array, remove it
				if (spore.destroyed) {
					window.spores.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 ((slideAttackBounds || player.isAirDashing && playerBounds) && !enemies[j].isHit && !enemies[j].isDying) {
			if (enemies[j].x > player.x && GameUtils.checkCollision(player.isAirDashing ? playerBounds : slideAttackBounds, enemyBounds)) {
				enemies[j].slideHit();
				continue;
			}
		}
		if (GameUtils.checkCollision(playerBounds, enemyBounds)) {
			if (!enemies[j].isHit && !enemies[j].isDying) {
				player.takeDamage();
			}
		}
	}
}
if (!game.saveManager) {
	game.saveManager = SaveManager;
	SaveManager.load();
}
// 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) {
		// Add shop update here if shop is open
		if (game.shopManager) {
			game.shopManager.update();
		}
		return;
	}
	// Update player and particles
	player.update();
	if (particleSystem) {
		particleSystem.update();
	}
	// Update arrows
	for (var i = arrows.length - 1; i >= 0; i--) {
		var arrow_to_check = arrows[i]; // Use a temporary variable for clarity
		arrow_to_check.update();
		// Check if arrow is outside screen boundaries and handle destruction
		if (arrow_to_check.x > GAME_WIDTH || arrow_to_check.x < 0 || arrow_to_check.y > GAME_HEIGHT || arrow_to_check.y < 0) {
			arrow_to_check.destroy();
			arrows.splice(i, 1);
			continue; // Arrow removed, skip to next iteration
		}
		// Fallback check: if arrow was destroyed by other means (e.g., enemy collision)
		// Note: Enemy collision logic in updateEnemies also splices. This is a safeguard.
		if (arrow_to_check.destroyed) {
			arrows.splice(i, 1);
			// No continue needed here as it's the last check for this arrow in this part of the loop
		}
	}
	for (var i = game.children.length - 1; i >= 0; i--) {
		var child = game.children[i];
		if (child instanceof CrumblePiece) {
			child.update();
		}
	}
	// Update arrow trail particles
	for (var k = arrowParticles.length - 1; k >= 0; k--) {
		var p = arrowParticles[k];
		p.update();
		// Check if particle is marked for destruction by its tween and is visually faded
		if (p.destroyQueued && p.sprite.alpha < 0.05) {
			p.destroy();
			arrowParticles.splice(k, 1);
		} else if (p.x < -50 || p.x > GAME_WIDTH + 50 || p.y < -50 || p.y > GAME_HEIGHT + 50) {
			// Fallback: destroy if way off-screen (e.g. if tween was interrupted or didn't complete)
			p.destroy();
			arrowParticles.splice(k, 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 (game.shopManager && game.shopManager.parent) {
		return;
	}
	if (!gameStarted) {
		// Don't process button clicks if shop is open
		if (game.shopManager && game.shopManager.parent) {
			return;
		}
		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) {
			// Check if the button has already been pressed
			if (game.playButtonPressed) {
				return;
			}
			game.playButtonPressed = true;
			// Stop the flashing animation
			if (playButtonFlashInterval) {
				LK.clearInterval(playButtonFlashInterval);
				playButtonFlashInterval = null;
				// Ensure button is fully visible before animation
				playButton.alpha = 1;
			}
			// Play start game sound effect
			LK.getSound('startgame').play();
			// Add scale animation
			tween(playButton, {
				scaleX: 0.8,
				scaleY: 0.8
			}, {
				duration: 150,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(playButton, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 250,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							// Start game after a 1 second delay
							LK.setTimeout(function () {
								startGame();
							}, 900);
						}
					});
				}
			});
		} else if (x >= tutorialButtonBounds.left && x <= tutorialButtonBounds.right && y >= tutorialButtonBounds.top && y <= tutorialButtonBounds.bottom) {
			// Add visual feedback animation
			tween(tutorialButton, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 100,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(tutorialButton, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 200,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							startTutorial();
						}
					});
				}
			});
			// Play menu select sound
			LK.getSound('menuselect').play();
			return;
		}
		return;
	}
	// Prevent input if player is dying or air dying
	if (player && (player.isDying || player.dyingInAir)) {
		return;
	}
	if (tutorialActive && tutorialManager) {
		tutorialManager.inputProcessed = false; // Reset the flag on touch end
	}
	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 > 50) {
			if (player.isOnGround) {
				player.slide();
			} else {
				player.airDash();
			}
		} else if (deltaX < -50) {
			// Left swipe - trigger bow shot
			player.shoot();
		}
	} else if (deltaY > 120) {
		// Downward swipe
		if (!player.isOnGround) {
			player.groundSlam(); // Call ground slam FIRST when in air
		} else {
			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;
	lastMoveY = y;
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.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; // Magnitude of velocity
	self.damage = 1;
	self.startX = 0; // Initial X position, set by Player
	self.startY = 0; // Initial Y position, set by Player
	self.rotation = 0; // Current angle of arrow in radians
	self.velocityX = 0; // X component of velocity
	self.velocityY = 0; // Y component of velocity
	var MAX_DISTANCE_FOR_FULL_HOMING = 400; // Pixels after which homing is at max strength (Reduced from 800)
	var MAX_TURN_RATE = 0.018; // Max radians per frame the arrow can turn (Further reduced from 0.035)
	self.update = function () {
		if (UPGRADE_REGISTRY.tracking_arrow > 0 && enemies && enemies.length > 0 && player) {
			// Added player check
			var closestEnemy = null;
			var minDistanceSq = Infinity;
			for (var i = 0; i < enemies.length; i++) {
				var enemy = enemies[i];
				// Condition to ensure enemy is in front of the player
				if (!enemy || enemy.isDying || enemy.isHit || enemy.destroyed || enemy.x < player.x) {
					continue;
				}
				var dx = enemy.x - self.x;
				var dy = enemy.y - self.y;
				var distSq = dx * dx + dy * dy;
				if (distSq < minDistanceSq) {
					minDistanceSq = distSq;
					closestEnemy = enemy;
				}
			}
			if (closestEnemy) {
				// No need to check closestEnemy.x < player.x again, already handled in loop
				var traveledDx = self.x - self.startX;
				var traveledDy = self.y - self.startY;
				var distanceTraveled = Math.sqrt(traveledDx * traveledDx + traveledDy * traveledDy);
				var homingFactor = 0;
				if (MAX_DISTANCE_FOR_FULL_HOMING > 0) {
					homingFactor = Math.min(1.0, distanceTraveled / MAX_DISTANCE_FOR_FULL_HOMING);
				}
				var targetAngle = Math.atan2(closestEnemy.y - self.y, closestEnemy.x - self.x);
				var angleDiff = targetAngle - self.rotation;
				while (angleDiff > Math.PI) {
					angleDiff -= 2 * Math.PI;
				}
				while (angleDiff < -Math.PI) {
					angleDiff += 2 * Math.PI;
				}
				var turnAmount = Math.max(-MAX_TURN_RATE, Math.min(MAX_TURN_RATE, angleDiff)) * homingFactor;
				self.rotation += turnAmount;
			}
		}
		self.velocityX = Math.cos(self.rotation) * self.speed;
		self.velocityY = Math.sin(self.rotation) * self.speed;
		self.x += self.velocityX * gameSpeedMultiplier;
		self.y += self.velocityY * gameSpeedMultiplier;
		self.sprite.rotation = self.rotation;
		// Add particle effect for tracking arrows
		if (UPGRADE_REGISTRY.tracking_arrow > 0 && LK.ticks % 3 === 0) {
			// Spawn every 3 ticks for a denser trail
			// Calculate spawn position slightly behind the arrow's tail
			var arrowTailOffsetX = Math.cos(self.rotation) * (self.sprite.width * 0.5 + 10);
			var arrowTailOffsetY = Math.sin(self.rotation) * (self.sprite.width * 0.5 + 10);
			var particleX = self.x - arrowTailOffsetX;
			var particleY = self.y - arrowTailOffsetY;
			var particle = new ArrowParticle(particleX, particleY);
			game.addChild(particle);
			arrowParticles.push(particle);
		}
		// Off-screen destruction logic is now handled in game.update
	};
	self.getBounds = function () {
		// A slightly more generous bounding box to account for rotation.
		// Uses the sprite's largest dimension to define a square box.
		var arrowWidth = self.sprite.width > 0 ? self.sprite.width : 120;
		var arrowHeight = self.sprite.height > 0 ? self.sprite.height : 31.2;
		var size = Math.max(arrowWidth, arrowHeight) * 0.6; // Make it a bit smaller than full diagonal
		return {
			left: self.x - size / 2,
			right: self.x + size / 2,
			top: self.y - size / 2,
			bottom: self.y + size / 2
		};
	};
	self.updateAmmo = function (amount) {
		// This method seems to belong to a UI element, not the Arrow projectile itself.
		// Keeping it here as per original structure, but it might be an error in original code.
		// Assuming self.ammoText exists if this method is called.
		if (self.ammoText) {
			self.ammoText.setText('x' + amount);
			if (amount >= 10) {
				self.ammoText.x = -150;
			} else {
				self.ammoText.x = -100;
			}
		}
	};
	return self;
});
var ArrowParticle = Container.expand(function (x, y) {
	var self = Container.call(this);
	self.sprite = self.attachAsset('pixel', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.2,
		// Increased particle size
		// Small particles
		scaleY: 0.2,
		// Increased particle size
		alpha: 0.9,
		// Initial alpha
		tint: 0xFF69B4 // Pink color for particles
	});
	self.x = x;
	self.y = y;
	self.driftX = (Math.random() - 0.5) * 1.5; // Random horizontal drift
	self.driftY = (Math.random() - 0.5) * 1.5 + 0.75; // Add a consistent downward push, ensuring particles mostly drift down
	self.destroyQueued = false;
	var durationMs = 200 + Math.random() * 200; // Lifespan of 200-400ms
	tween(self.sprite, {
		alpha: 0
	}, {
		duration: durationMs,
		easing: tween.linear,
		// Simple linear fade
		onFinish: function onFinish() {
			self.destroyQueued = true;
		}
	});
	self.update = function () {
		if (self.destroyQueued) {
			// If marked for destruction by tween, do nothing further here.
			// game.update will handle actual removal.
			return;
		}
		self.x += self.driftX;
		self.y += self.driftY;
		// Particles also need to scroll with the screen
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
	};
	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;
});
// CrumblePiece class
var CrumblePiece = Container.expand(function (pieceNum) {
	var self = Container.call(this);
	self.sprite = self.attachAsset('crumblepiece' + pieceNum, {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocityX = 0;
	self.velocityY = 0;
	self.rotationSpeed = 0;
	self.update = function () {
		// Apply physics
		self.velocityY += 0.5; // gravity
		self.x += self.velocityX;
		self.y += self.velocityY;
		self.rotation += self.rotationSpeed;
		// Destroy if off screen
		if (self.y > GAME_HEIGHT || self.x < -100) {
			self.destroy();
		}
	};
	return self;
});
var CustomText = Container.expand(function (text, options) {
	var self = Container.call(this);
	// Basic settings
	self.size = options.size || 70;
	self.scaleSize = self.size / 70;
	self.letterSpacing = (options.letterSpacing || 10) * self.scaleSize;
	self.fill = options.fill || 0xFFFFFF;
	self.wordWrap = options.wordWrap || false;
	self.wordWrapWidth = options.wordWrapWidth || 800;
	self.lineHeight = (options.lineHeight || 1.2) * self.size;
	self.letters = [];
	self.letterPool = {}; // Pool of letter sprites by character
	self.text = '';
	self.width = 0;
	self.height = 0; // Track height for multi-line text
	// Get asset name for character - keep original mapping logic
	self.getAssetForChar = function (_char) {
		if (_char.match(/[a-z]/i)) {
			return 'letter_' + _char.toLowerCase();
		} else if (_char.match(/[0-9]/)) {
			return 'number_' + _char;
		} else {
			switch (_char) {
				case '.':
					return 'period_mark';
				case ',':
					return 'comma_mark';
				case '!':
					return 'exclamation_mark';
				case '?':
					return 'question_mark';
				case '+':
					return 'addition_mark';
				case '*':
					return 'multiplication_mark';
				case ' ':
					return null;
				default:
					return null;
			}
		}
	};
	// Get or create sprite from pool
	self.getOrCreateSprite = function (_char2) {
		// Initialize pool for this character if needed
		if (!self.letterPool[_char2]) {
			self.letterPool[_char2] = [];
		}
		// Try to find an inactive sprite
		var sprite = self.letterPool[_char2].find(function (s) {
			return !s.active;
		});
		// Create new sprite if none available
		if (!sprite) {
			var assetName = self.getAssetForChar(_char2);
			if (assetName) {
				sprite = self.attachAsset(assetName, {
					anchorX: 0.5,
					anchorY: 0.5,
					scaleX: self.scaleSize,
					scaleY: self.scaleSize
				});
				if (self.fill) {
					sprite.tint = self.fill;
				}
				sprite.active = false;
				sprite.alpha = 0;
				// Store character type for position adjustments
				sprite.charType = _char2;
				self.letterPool[_char2].push(sprite);
			}
		}
		return sprite;
	};
	// Calculate the width a text would take
	self.calculateTextWidth = function (text) {
		var width = 0;
		for (var i = 0; i < text.length; i++) {
			var _char4 = text[i];
			if (_char4 === ' ') {
				width += 40 * self.scaleSize;
			} else {
				width += 50 * self.scaleSize + self.letterSpacing;
			}
		}
		return width;
	};
	// Break text into lines for word wrap
	self.breakTextIntoLines = function (text) {
		// First handle explicit line breaks
		if (text.includes('\n')) {
			var explicitLines = text.split('\n');
			var result = [];
			// Process each explicit line for word wrapping
			for (var j = 0; j < explicitLines.length; j++) {
				if (self.wordWrap) {
					var wrappedLines = this.breakTextIntoLines(explicitLines[j]);
					result = result.concat(wrappedLines);
				} else {
					result.push(explicitLines[j]);
				}
			}
			return result;
		}
		if (!self.wordWrap) {
			return [text];
		}
		var words = text.split(' ');
		var lines = [];
		var currentLine = '';
		var currentWidth = 0;
		var spaceWidth = 40 * self.scaleSize;
		for (var i = 0; i < words.length; i++) {
			var word = words[i];
			var wordWidth = self.calculateTextWidth(word);
			// Check if adding this word would exceed the line width
			if (currentWidth + wordWidth > self.wordWrapWidth && currentLine !== '') {
				lines.push(currentLine.trim());
				currentLine = word + ' ';
				currentWidth = wordWidth + spaceWidth;
			} else {
				currentLine += word + ' ';
				currentWidth += wordWidth + spaceWidth;
			}
		}
		// Add the last line if there's anything left
		if (currentLine.trim() !== '') {
			lines.push(currentLine.trim());
		}
		return lines;
	};
	// Set text content with sprite pooling
	self.setText = function (newText) {
		// Always convert to string
		newText = String(newText);
		// Deactivate all current letters
		self.letters.forEach(function (letter) {
			letter.active = false;
			letter.alpha = 0;
		});
		self.letters = [];
		// Break text into lines if word wrap is enabled
		var lines = self.breakTextIntoLines(newText);
		var maxLineWidth = 0;
		// Position tracking
		var currentX = 0;
		var currentY = 0;
		var lineCount = 0;
		// Process each line
		for (var lineIndex = 0; lineIndex < lines.length; lineIndex++) {
			var line = lines[lineIndex];
			currentX = 0; // Reset X position for each line
			// Create/reuse sprites for each character in this line
			for (var i = 0; i < line.length; i++) {
				var _char3 = line[i];
				// Handle spaces
				if (_char3 === ' ') {
					currentX += 40 * self.scaleSize;
					continue;
				}
				// Get or create sprite
				var sprite = self.getOrCreateSprite(_char3);
				if (sprite) {
					sprite.active = true;
					sprite.alpha = 1;
					sprite.x = currentX;
					sprite.y = currentY;
					// Add vertical position adjustment for punctuation
					if (_char3 === '.' || _char3 === ',') {
						sprite.y += 20 * self.scaleSize; // Move periods and commas down
					} else if (_char3 === '*') {
						sprite.y += 10 * self.scaleSize; // Move multiplication mark down slightly
					}
					self.letters.push(sprite);
					currentX += 50 * self.scaleSize + self.letterSpacing;
				}
			}
			// Track the maximum line width
			if (currentX > maxLineWidth) {
				maxLineWidth = currentX;
			}
			// Move to next line
			if (lineIndex < lines.length - 1) {
				currentY += self.lineHeight;
				lineCount++;
			}
		}
		// Update width and height properties
		self.width = maxLineWidth;
		self.height = (lineCount + 1) * self.lineHeight;
		if (self.letters.length === 0) {
			self.width = 0;
			self.height = 0;
		}
		// Handle anchor alignment
		if (options.anchorX === 1) {
			// Right-aligned text
			for (var i = 0; i < self.letters.length; i++) {
				self.letters[i].x -= self.width;
			}
		} else if (options.anchorX === 0.5) {
			// Center-aligned each line
			var currentLine = 0;
			var lineStart = 0;
			var lineEnd = 0;
			var lineWidth = 0;
			for (var i = 0; i < self.letters.length; i++) {
				// Check if we're in a new line
				var letterY = self.letters[i].y;
				if (i === 0 || letterY !== self.letters[lineStart].y) {
					// Process previous line if this isn't the first letter
					if (i > 0) {
						lineEnd = i - 1;
						lineWidth = self.letters[lineEnd].x + 50 * self.scaleSize / 2;
						// Center this line
						for (var j = lineStart; j <= lineEnd; j++) {
							self.letters[j].x -= lineWidth / 2;
						}
					}
					// Start tracking new line
					lineStart = i;
				}
			}
			// Process the last line
			if (lineStart < self.letters.length) {
				lineEnd = self.letters.length - 1;
				lineWidth = self.letters[lineEnd].x + 50 * self.scaleSize / 2;
				// Center this line
				for (var j = lineStart; j <= lineEnd; j++) {
					self.letters[j].x -= lineWidth / 2;
				}
			}
		}
		// Store current text
		self.text = newText;
	};
	// Cleanup method
	self.destroy = function () {
		// Properly destroy all pooled sprites
		Object.values(self.letterPool).forEach(function (pool) {
			pool.forEach(function (sprite) {
				if (sprite.destroy) {
					sprite.destroy();
				}
			});
		});
		self.letterPool = {};
		self.letters = [];
		Container.prototype.destroy.call(this);
	};
	// Initialize with provided text
	self.setText(text);
	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;
	// Add mushroom-specific properties
	self.isAttacking = false;
	self.attackTimer = 0;
	self.attackDuration = 100; // 60 frames for attack animation
	self.attackCooldown = 180; // Cooldown before next attack
	self.attackCooldownTimer = 0;
	self.sporesReleased = false; // Flag to ensure spores only released once per attack
	// 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.11;
	// Define all animation arrays based on enemy type
	if (self.type === 'mushroom') {
		// Mushroom-specific properties
		self.runFrame = 0;
		self.speed = 6;
		self.runAnimation = ['mushroomrun1', 'mushroomrun2', 'mushroomrun3', 'mushroomrun4', 'mushroomrun5', 'mushroomrun6', 'mushroomrun7', 'mushroomrun8'];
		self.hitAnimation = ['mushroomhit1', 'mushroomhit2']; // New hit animations
		self.dieAnimation = ['mushroomdie1', 'mushroomdie2', 'mushroomdie3', 'mushroomdie4']; // New death animations
	} else 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);
		}
	};
	self.initMushroomSprites = 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);
		}
		for (var i = 0; i < 5; i++) {
			var sprite = self.attachAsset('mushroomattack' + (i + 1), {
				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.75;
			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) && self.throwingTimer > Math.floor(self.throwingDuration * 0.8) - 2 && !self.swordThrown) {
			// Flag to ensure we only throw once during this window
			self.swordThrown = true;
			// Create and initialize sword with more robust positioning
			var sword = new SkeletonSword();
			sword.x = self.x - 100; // Fixed position relative to skeleton
			sword.y = self.y - 100;
			// Explicitly set initial velocity independent of gameSpeedMultiplier
			sword.constantVelocity = 8; // Fixed velocity
			// Add the sword to the game and tracking array
			game.addChild(sword);
			skeletonSwords.push(sword);
			// Show throwing frame
			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.swordThrown = false; // Reset the flag
			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.swordThrown = false; // Initialize flag here
				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;
	};
	self.updateMushroomNormalState = function () {
		// Check if should attack
		if (self.checkMushroomAttack()) {
			return; // Skip normal update if starting attack
		}
		// Decrease attack cooldown timer
		if (self.attackCooldownTimer > 0) {
			self.attackCooldownTimer--;
		}
		// Keep existing normal state code
		// 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 mushroom 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
		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.updateMushroomHitState = function () {
		// Same movement physics as before
		self.x += self.throwBackSpeed;
		self.throwBackDistance += Math.abs(self.throwBackSpeed);
		self.throwBackSpeed *= 0.95;
		// Platform collision check (same as before)
		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 && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
						self.currentPlatform = otherPlatform;
						foundAnotherPlatform = true;
						break;
					}
				}
				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.75;
			self.y += self.velocityY;
			self.checkPlatformCollision();
		}
		// Show hit animation - Sequential progression through hit frames
		var hitOffset = self.runAnimation.length;
		// Calculate hit frame based on timer
		// If we have 2 hit frames and hitTimer starts at 35, show first frame for first half, second frame for second half
		var hitProgress = 1 - self.hitTimer / 35; // Normalize from 0 to 1
		var hitFrame = Math.min(Math.floor(hitProgress * self.hitAnimation.length), self.hitAnimation.length - 1);
		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';
		}
	};
	self.updateMushroomDyingState = function () {
		// Same movement physics as before
		self.x += self.throwBackSpeed;
		// Platform collision check (same as before)
		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 && Math.abs(self.y - (otherPlatform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
						self.currentPlatform = otherPlatform;
						foundAnotherPlatform = true;
						break;
					}
				}
				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 - Updated to progress through all frames
		var dieOffset = self.runAnimation.length + self.hitAnimation.length;
		// Progress frame every 15 frames (similar to other enemy death animations)
		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();
			}
		}
	};
	self.checkMushroomAttack = function () {
		if (self.type !== 'mushroom' || self.isHit || self.isDying || self.isAttacking || self.attackCooldownTimer > 0) {
			return false;
		}
		// Attack when player is in range (horizontal and vertical distance check)
		var distanceX = Math.abs(player.x - self.x);
		var distanceY = Math.abs(player.y - self.y);
		if (distanceX < 800 && distanceY < 700 && self.x > player.x) {
			// Store original position relative to platform BEFORE changing state
			self.platformOffset = self.currentPlatform ? self.x - self.currentPlatform.x : 0;
			// Set attacking state
			self.isAttacking = true;
			self.attackTimer = self.attackDuration;
			self.sporesReleased = false;
			self.originalSpeed = self.speed;
			// Don't change position here - we'll maintain platform-relative position
			return true;
		}
		return false;
	};
	// Add mushroom attack animation update
	self.updateMushroomAttackState = function () {
		// Get offset to find attack animation sprites
		var attackOffset = self.runAnimation.length + self.hitAnimation.length + self.dieAnimation.length;
		// Calculate which frame to display based on attack progress
		var frameProgress = (self.attackDuration - self.attackTimer) / self.attackDuration;
		var attackFrame = Math.min(Math.floor(frameProgress * 5), 4);
		// Show current attack frame
		self.sprites[attackOffset + attackFrame].alpha = 1;
		// Play hiss sound when attack animation starts
		if (attackFrame === 0 && self.attackTimer === self.attackDuration - 1) {
			LK.getSound('mushroomhiss').play();
		}
		// Release spores at frame 3 (middle of animation)
		if (attackFrame === 3 && !self.sporesReleased) {
			self.releaseSpores();
			self.sporesReleased = true;
		}
		// If on a platform, maintain position relative to that platform
		if (self.currentPlatform) {
			// Use stored platform offset (which is 0 if not previously set)
			self.x = self.currentPlatform.x + (self.platformOffset || 0);
		} else {
			// If not on a platform, just match platform movement speed
			self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		}
		// Count down attack timer
		self.attackTimer--;
		// End attack when timer expires
		if (self.attackTimer <= 0) {
			self.isAttacking = false;
			self.speed = self.originalSpeed || 6;
			self.attackCooldownTimer = self.attackCooldown;
		}
	};
	// Function to release spores in the specified pattern
	self.releaseSpores = function () {
		// Create 5 spores in pattern described
		var directions = [{
			x: 1,
			y: 0
		},
		// Right
		{
			x: -1,
			y: 0
		},
		// Left
		{
			x: 0,
			y: -1
		},
		// Up
		{
			x: 0.7,
			y: -0.7
		},
		// Up-right diagonal
		{
			x: -0.7,
			y: -0.7
		} // Up-left diagonal
		];
		// Calculate an offset position IN FRONT of the mushroom
		// This compensates for the platform movement and animation delay
		var offsetX = self.x; // Add a significant forward offset
		var offsetY = self.y - 50; // Slightly above as before
		// Play mushroom burst sound effect
		LK.getSound('mushroomburst').play();
		for (var i = 0; i < directions.length; i++) {
			var spore = new Spore();
			// Position spores at the offset position
			spore.x = offsetX;
			spore.y = offsetY;
			// Don't add PLATFORM_SPEED to the velocityX, as that's causing over-compensation
			spore.velocityX = directions[i].x * spore.speed;
			spore.velocityY = directions[i].y * spore.speed;
			// Add to game
			game.addChild(spore);
			// Add to a global array for tracking if needed
			if (!window.spores) {
				window.spores = [];
			}
			window.spores.push(spore);
		}
	};
	// 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;
			if (self.x >= leftEdge && self.x <= rightEdge) {
				if (Math.abs(self.y - (platform.y - ENEMY_PLATFORM_OFFSET)) < 5) {
					// Add this check:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					// Add this check:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					onAnyPlatform = true;
					self.currentPlatform = platform;
					self.y = platform.y - ENEMY_PLATFORM_OFFSET;
					self.isOnGround = true;
					self.velocityY = 0;
					return true;
				}
				if (self.velocityY > 0 && self.y < platform.y - ENEMY_PLATFORM_OFFSET && self.y + self.velocityY >= platform.y - ENEMY_PLATFORM_OFFSET) {
					// Add this check here too:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					// Add this check here too:
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					// Rest of the collision code remains the same
					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;
			// Check if player has slide damage upgrade
			if (player.slideDamage && player.slideDamageTimer <= 0) {
				// If upgraded, treat as regular attack
				self.hitType = 'attack';
				player.slideDamageTimer = player.slideDamageCooldown;
			} else {
				// Otherwise just knockback
				self.hitType = 'slide';
			}
			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') {
				// (existing eyeball trajectory calculation)
				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;
				}
				self.velocityY = verticalTrajectory;
			}
			// Play the appropriate hit sound based on enemy type
			if (self.type === 'eyeball') {
				LK.getSound('eyeballhit').play();
			} else if (self.type === 'skeleton') {
				LK.getSound('skeletonhit').play();
			} else if (self.type === 'mushroom' && player.slideDamage) {
				LK.getSound('mushroomdie').play();
			} else if (self.type === 'mushroom') {
				LK.getSound('mushroombounce').play();
			} else {
				LK.getSound('enemyhit').play();
			}
			// 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
			}
			// Play appropriate hit sound based on enemy type
			if (self.type === 'mushroom') {
				LK.getSound('mushroomdie').play();
			}
			// 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;
		}
		// Apply loot multiplier from Fortune Finder upgrade
		var lootMultiplier = player.lootMultiplier || 1.0;
		// Determine number of coins to drop (1-5) adjusted by loot modifier and player's upgrade
		var coinCount = Math.floor(GameUtils.randomRange(1, 6) * self.lootModifier * lootMultiplier);
		// Small chance for jewels - increased by loot modifier AND player upgrade
		var dropJewel = Math.random();
		var jewelChance = 0.02 * self.lootModifier * lootMultiplier; // Base 2% chance multiplied by modifiers
		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 if (self.type === 'mushroom') {
			// Mushroom logic - modified to include attack state
			if (self.isHit) {
				self.updateMushroomHitState();
			} else if (self.isDying) {
				self.updateMushroomDyingState();
			} else if (self.isAttacking) {
				self.updateMushroomAttackState();
			} else {
				self.updateMushroomNormalState();
			}
		} 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 if (self.type === 'mushroom') {
		self.initMushroomSprites();
	} 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;
	// NEW METHOD: Update max health and add/remove heart sprites as needed
	self.updateMaxHealth = function (newMaxHealth) {
		var heartSpacing = 80;
		// Add new hearts if needed
		if (newMaxHealth > self.maxHealth) {
			for (var i = self.maxHealth; i < newMaxHealth; i++) {
				var heart = self.attachAsset('heart', {
					anchorX: 0.5,
					anchorY: 0.5,
					tint: 0xCC0000
				});
				self.hearts.push(heart);
			}
		}
		// Remove hearts if needed (unlikely, but for completeness)
		else if (newMaxHealth < self.maxHealth) {
			for (var i = self.maxHealth - 1; i >= newMaxHealth; i--) {
				if (self.hearts[i]) {
					self.hearts[i].destroy();
					self.hearts.pop();
				}
			}
		}
		// Update max health value
		self.maxHealth = newMaxHealth;
		// Recalculate total width and reposition all hearts
		totalWidth = (self.maxHealth - 1) * heartSpacing;
		for (var i = 0; i < self.hearts.length; i++) {
			self.hearts[i].x = i * heartSpacing - totalWidth / 2;
		}
	};
	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) {
			// Get loot multiplier from player
			var lootMultiplier = player.lootMultiplier || 1.0;
			// Spawn health potion with low chance - improved by loot multiplier
			if (Math.random() < 0.05 * (player.potionDropChance || 1.0)) {
				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 - improved by loot multiplier
			if (Math.random() < 0.25 * (player.arrowDropChance || 1.0)) {
				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 - quantity affected by loot multiplier
			var coinCount = Math.floor(GameUtils.randomRange(1, 9) * lootMultiplier);
			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;
});
var CrumblePlatform = Platform.expand(function () {
	var self = Platform.call(this);
	// Override the sprite
	self.sprite = self.attachAsset('crumbleplatform', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.originalY = 0;
	// Crumbling state
	self.isCrumbling = false;
	self.crumbleDelay = 60; // frames before breaking
	self.crumbleTimer = 0;
	self.triggered = false;
	self.broken = false; // Add this flag to prevent multiple breaks
	// Override update method
	self.update = function () {
		// Normal platform movement
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		// Handle crumbling state
		if (self.isCrumbling && !self.broken) {
			// Quick shake effect
			var shakeIntensity = Math.min(6, 2 + (self.crumbleDelay - self.crumbleTimer) / 5);
			self.sprite.y = Math.random() * shakeIntensity * 2 - shakeIntensity;
			self.crumbleTimer--;
			// Only break when the timer is actually finished
			if (self.crumbleTimer <= 0) {
				self["break"]();
			}
		}
		// Check if off screen
		if (self.x < -500) {
			self.destroy();
		}
	};
	self.trigger = function () {
		if (!self.triggered && !self.broken) {
			LK.getSound('platformcrumble').play();
			self.triggered = true;
			self.isCrumbling = true;
			self.crumbleTimer = self.crumbleDelay;
			self.sprite.originalY = self.sprite.y;
		}
	};
	self["break"] = function () {
		if (self.broken) {
			return;
		}
		self.broken = true;
		LK.getSound('rocksfall').play();
		// CRITICAL FIX: Remove from platforms array BEFORE updating entities
		var index = platforms.indexOf(self);
		if (index !== -1) {
			platforms.splice(index, 1);
		}
		// Force all entities on this platform to fall
		if (player && player.currentPlatform === self) {
			player.isOnGround = false;
			player.currentPlatform = null;
			player.velocityY = 3; // Stronger initial velocity
		}
		// Check all enemies too
		for (var i = 0; i < enemies.length; i++) {
			if (enemies[i].currentPlatform === self) {
				enemies[i].isOnGround = false;
				enemies[i].currentPlatform = null;
				enemies[i].velocityY = 1;
			}
		}
		// Spawn 10 pieces distributed across the platform width
		for (var i = 0; i < 10; i++) {
			var pieceNum = Math.floor(Math.random() * 3) + 1;
			var piece = new CrumblePiece(pieceNum);
			// Distribute pieces across the platform width
			var distributionOffset = i / 9 * PLATFORM_WIDTH - PLATFORM_HALF_WIDTH;
			piece.x = self.x + distributionOffset + (Math.random() * 80 - 40); // Add small random offset
			piece.y = self.y + (Math.random() * 30 - 15); // Small vertical variation
			// Vary the falling speeds and behaviors
			piece.velocityX = -PLATFORM_SPEED + (Math.random() * 2 - 1); // Slight x variation
			piece.velocityY = Math.random() * 3; // Some pieces fall faster than others
			piece.rotationSpeed = (Math.random() - 0.5) * 0.25; // More varied rotation
			game.addChild(piece);
		}
		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'];
	self.airDashAnimation = ['playerairdash'];
	self.groundSlamAnimation = ['playergroundslam1', 'playergroundslam2', 'playergroundslam3', 'playergroundslam4', 'playergroundslam5', 'playergroundslam6'];
	// 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.092;
	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 = 100;
	self.standUpDuration = 30;
	self.slideCooldown = 30;
	self.lastSlideTime = 0;
	self.slideSpeedMultiplier = 1.75;
	self.slideDamage = false; // Will be set based on upgrade
	self.slideDamageCooldown = 15; // Frames between slide damage hits
	self.slideDamageTimer = 0; // Current cooldown timer
	self.normalHitboxHeight = self.hitboxHeight; // Store original height
	self.slideHitboxHeight = 200; // Make it taller
	self.slideHitboxYOffset = 150; // Move it much lower
	self.isAirDashing = false;
	self.canAirDash = true; // Reset when landing
	self.airDashTimer = 0;
	self.airDashDuration = 100; // Half of slide duration since it's faster
	self.airDashSpeedMultiplier = 2.3; // Slightly faster than slide
	self.shadowImages = [];
	// Add ground slam properties
	self.isGroundSlamming = false;
	self.groundSlamFrame = 0;
	self.groundSlamPhase = "start"; // "start", "air", "impact"
	self.canGroundSlam = true; // Can only ground slam once per jump
	self.groundSlamAnimationSpeed = 0.15;
	self.groundSlamImpactTimer = 0;
	// Initialize animation sprites
	self.initAnimations = function () {
		// Run animations
		for (var i = 0; i < self.runAnimation.length; i++) {
			var sprite = self.attachAsset(self.runAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = i === 0 ? 1 : 0;
			self.sprites.push(sprite);
		}
		// Jump animations
		for (var i = 0; i < self.jumpAnimation.length; i++) {
			var sprite = self.attachAsset(self.jumpAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// Attack animations
		for (var i = 0; i < self.attackAnimation.length; i++) {
			var sprite = self.attachAsset(self.attackAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		// 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);
		}
		// Add air dash animation
		for (var i = 0; i < self.airDashAnimation.length; i++) {
			var sprite = self.attachAsset(self.airDashAnimation[i], {
				anchorX: 0.5,
				anchorY: 0.5
			});
			sprite.alpha = 0;
			self.sprites.push(sprite);
		}
		for (var i = 0; i < self.groundSlamAnimation.length; i++) {
			var sprite = self.attachAsset(self.groundSlamAnimation[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];
			// Use EXACT platform bounds for edge detection - no added buffer
			var leftEdge = platform.x - PLATFORM_HALF_WIDTH;
			var rightEdge = platform.x + PLATFORM_HALF_WIDTH;
			// Check if player is within horizontal bounds of platform
			if (self.x >= leftEdge && self.x <= rightEdge) {
				// If we're at platform height and not jumping
				if (Math.abs(self.y - (platform.y - PLAYER_PLATFORM_OFFSET)) < 5 && !self.isJumping) {
					onAnyPlatform = true;
					// Add this check for crumble platforms
					if (platform instanceof CrumblePlatform) {
						platform.trigger();
					}
					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 - use EXACT edge detection
		if (self.currentPlatform) {
			// Add check for destroyed platform
			if (self.currentPlatform.destroyed || self.currentPlatform.broken) {
				self.isOnGround = false;
				self.currentPlatform = null;
				if (self.velocityY === 0) {
					self.velocityY = 0.1;
				}
				return;
			}
			// Use EXACT platform bounds - no buffer for edge detection when moving
			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;
					}
					// Use EXACT platform bounds for other platforms too
					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 () {
		// First, check if current platform reference is valid
		if (self.currentPlatform && (self.currentPlatform.destroyed || self.currentPlatform instanceof CrumblePlatform && self.currentPlatform.broken)) {
			// Force falling state if platform reference is to a broken/destroyed platform
			self.isOnGround = false;
			self.currentPlatform = null;
			if (self.velocityY < 1) {
				self.velocityY = 1; // Ensure downward movement begins
			}
		}
		// Then apply normal physics
		if (!self.isOnGround || self.isJumping) {
			// Apply velocity and gravity
			self.y += self.velocityY;
			self.velocityY += 0.89; // Increase gravity effect
			// Check for landing on platforms
			var hitPlatform = self.checkPlatformCollision();
		}
		// 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
			SaveManager.save();
			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;
			// Create dust particles behind player during slide (every 5 frames)
			if (LK.ticks % 5 === 0) {
				// Create 2-3 particles per emission
				var particleCount = 2 + Math.floor(Math.random() * 2);
				for (var i = 0; i < particleCount; i++) {
					var particle = game.addChild(LK.getAsset('pixel', {
						anchorX: 0.5,
						anchorY: 0.5,
						scaleX: 0.1 + Math.random() * 0.2,
						scaleY: 0.1 + Math.random() * 0.2,
						alpha: 0.7 + Math.random() * 0.3
					}));
					// Position particles behind player near ground level
					particle.x = self.x + 120 - Math.random() * 50;
					particle.y = self.y + 145 + Math.random() * 40;
					// Set velocities with slight variation
					particle.velocityX = -Math.random() * 3 - 1;
					particle.velocityY = -Math.random() * 2;
					// Add slight random rotation
					particle.rotationSpeed = (Math.random() - 0.5) * 0.1;
					particle.lifespan = 20 + Math.floor(Math.random() * 15);
					// Add update method to particles
					particle.update = function () {
						this.x += this.velocityX;
						this.y += this.velocityY;
						this.rotation += this.rotationSpeed;
						this.lifespan--;
						if (this.lifespan < 10) {
							this.alpha -= 0.1;
						}
						if (this.lifespan <= 0) {
							this.destroy();
						}
					};
				}
			}
		} 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.updateAirDashAnimation = function () {
		// Calculate offset to find air dash animation sprite
		var airDashOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length + self.deathAnimation.length + self.bowAnimation.length;
		// Show air dash sprite
		self.sprites[airDashOffset].alpha = 1;
		// Add quick clockwise rotation during air dash
		if (self.airDashTimer === self.airDashDuration) {
			// Reset rotation at start of dash
			self.rotation = 0;
			// Play air dash sound effect
			LK.getSound('airdash').play();
			// Start rotation tween - make it happen faster and then hold value
			tween(self, {
				rotation: Math.PI / 8 // Rotate about 30 degrees clockwise
			}, {
				duration: 100,
				// Much faster rotation (300ms instead of ~1.5s)
				easing: tween.easeOut
				// No onFinish needed since we'll hold this rotation for the dash duration
			});
		}
		// Keep vertical velocity at 0 during dash
		self.velocityY = 0;
		// Update shadow positions with slight motion effect
		self.shadowImages.forEach(function (shadow, index) {
			// Add slight vertical variation to shadows for more dynamic feel
			var verticalOffset = Math.sin(self.airDashTimer * 0.1 + index * 0.5) * 10;
			// Adjust spacing and position for a more dramatic trail effect
			shadow.x = self.x - (80 * (index + 1) + (self.airDashDuration - self.airDashTimer) * 0.5);
			shadow.y = self.y + verticalOffset;
			shadow.alpha = 0.3 - index * 0.05; // More gradual fade out for longer trails
			// Add slight rotation to shadows
			shadow.rotation = self.rotation * (0.6 - index * 0.1); // Less rotation for farther shadows
		});
		self.airDashTimer--;
		if (self.airDashTimer <= 0) {
			self.isAirDashing = false;
			gameSpeedMultiplier = 1.0;
			// Reset rotation back to normal when dash ends
			tween(self, {
				rotation: 0
			}, {
				duration: 300,
				easing: tween.easeOut
			});
			// Clean up shadows
			self.shadowImages.forEach(function (shadow) {
				if (shadow.parent) {
					shadow.destroy();
				}
			});
			self.shadowImages = [];
		}
	};
	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;
					arrow.startX = arrow.x;
					arrow.startY = arrow.y;
					arrow.rotation = 0; // Default right direction for tutorial
					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;
					arrow.startX = arrow.x;
					arrow.startY = arrow.y;
					// Smart initial aiming for tracking arrows
					var initialRotation = 0; // Default straight right
					if (UPGRADE_REGISTRY.tracking_arrow > 0 && enemies && enemies.length > 0) {
						// Find the best target within a reasonable cone
						var bestTarget = null;
						var bestScore = -1;
						var coneAngle = Math.PI / 3; // 60 degree cone (30 degrees up/down from horizontal)
						var maxDistance = 1000; // Maximum distance to consider for initial aiming
						for (var i = 0; i < enemies.length; i++) {
							var enemy = enemies[i];
							if (!enemy || enemy.isDying || enemy.isHit || enemy.destroyed || enemy.x < self.x) {
								continue;
							}
							var dx = enemy.x - arrow.x;
							var dy = enemy.y - arrow.y;
							var distance = Math.sqrt(dx * dx + dy * dy);
							var angleToEnemy = Math.atan2(dy, dx);
							// Check if enemy is within cone and reasonable distance
							if (distance <= maxDistance && Math.abs(angleToEnemy) <= coneAngle / 2) {
								// Score based on proximity and how centered they are in the cone
								var centeredness = 1 - Math.abs(angleToEnemy) / (coneAngle / 2);
								var proximity = 1 - distance / maxDistance;
								var score = centeredness * 0.6 + proximity * 0.4;
								if (score > bestScore) {
									bestScore = score;
									bestTarget = enemy;
								}
							}
						}
						// If we found a good target, aim at it initially
						if (bestTarget) {
							initialRotation = Math.atan2(bestTarget.y - arrow.y, bestTarget.x - arrow.x);
						}
					}
					arrow.rotation = initialRotation;
					arrow.sprite.rotation = initialRotation;
					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.updateGroundSlamAnimation = function () {
		// Calculate offset to find ground slam animation sprites
		var groundSlamOffset = self.runAnimation.length + self.jumpAnimation.length + self.attackAnimation.length + self.airAttackAnimation.length + self.slideAnimation.length + self.standUpAnimation.length + self.deathAnimation.length + self.bowAnimation.length + self.airDashAnimation.length;
		// Create hitbox for the slam (used both in air and on impact)
		var slamHitbox = {
			left: self.x - 200,
			right: self.x + 200,
			top: self.y,
			bottom: self.y + 200
		};
		// Update animation based on phase
		if (self.groundSlamPhase === "start") {
			// Show first frame briefly
			self.sprites[groundSlamOffset].alpha = 1;
			self.animationCounter += self.attackAnimationSpeed;
			if (self.animationCounter >= 1) {
				self.groundSlamPhase = "air";
				self.animationCounter = 0;
			}
		} else if (self.groundSlamPhase === "air") {
			// Alternate between frames 2 and 3 while falling
			self.animationCounter += self.attackAnimationSpeed;
			if (self.animationCounter >= 1) {
				self.animationCounter = 0;
				self.groundSlamFrame = (self.groundSlamFrame + 1) % 2;
			}
			self.sprites[groundSlamOffset + 1 + self.groundSlamFrame].alpha = 1;
			// Check for enemies and breakables during descent
			self.checkGroundSlamHits(slamHitbox);
			// Check if we've hit the ground
			if (self.isOnGround) {
				self.groundSlamPhase = "impact";
				self.groundSlamFrame = 0;
				self.groundSlamImpactTimer = 30;
				self.animationCounter = 0;
				// Apply screen shake effect on impact
				applyScreenShake();
				LK.getSound('groundimpact').play();
				// Do one final more powerful hit check on impact
				self.applyGroundSlamDamage();
			}
		} else if (self.groundSlamPhase === "impact") {
			// Cycle through frames 4-6 for impact
			self.animationCounter += self.attackAnimationSpeed * 1.5;
			if (self.animationCounter >= 1) {
				self.animationCounter = 0;
				self.groundSlamFrame++;
				if (self.groundSlamFrame >= 3) {
					self.isGroundSlamming = false;
					return;
				}
			}
			self.sprites[groundSlamOffset + 3 + self.groundSlamFrame].alpha = 1;
			// Count down timer
			self.groundSlamImpactTimer--;
			if (self.groundSlamImpactTimer <= 0) {
				self.isGroundSlamming = false;
			}
		}
	};
	self.applyGroundSlamDamage = function () {
		// Create larger hitbox for ground slam
		var slamHitbox = {
			left: self.x - 300,
			right: self.x + 300,
			top: self.y,
			bottom: self.y + 200
		};
		// Add particle spray effect when landing
		self.createGroundSlamParticles();
		// Check all enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (!enemy.isHit && !enemy.isDying) {
				var enemyBounds = enemy.getBounds();
				// If enemy is on ground and in range
				if (GameUtils.checkCollision(slamHitbox, enemyBounds) && (enemy.isOnGround || enemy.y > self.y)) {
					enemy.hit();
					// Play appropriate sound
					if (enemy.type === 'eyeball') {
						LK.getSound('eyeballhit').play();
					} else if (enemy.type === 'skeleton') {
						LK.getSound('skeletonhit').play();
					} else {
						LK.getSound('enemyhit').play();
					}
					// Create more particles for impact
					particleSystem.emitFromHit(enemy.x + 250, enemy.y, self.x, 5, enemy.type);
				}
			}
		}
	};
	// Add a new method to create ground slam particles
	self.createGroundSlamParticles = function () {
		// Create a large number of particles for dramatic effect
		var particleCount = 25 + Math.floor(Math.random() * 10);
		for (var i = 0; i < particleCount; i++) {
			var particle = game.addChild(LK.getAsset('pixel', {
				anchorX: 0.5,
				anchorY: 0.5,
				scaleX: 0.1 + Math.random() * 0.3,
				scaleY: 0.1 + Math.random() * 0.3,
				alpha: 0.7 + Math.random() * 0.3,
				// Keep particles untinted (white)
				tint: 0xFFFFFF
			}));
			// Position particles at the point of impact
			particle.x = self.x + Math.random() * 300 - 150; // Spread horizontally
			particle.y = self.y + 145; // Just above ground level
			// Set velocities with upward trajectory
			var angle = Math.PI / 2 + (Math.random() - 0.5) * 0.8; // Mostly upward with spread
			var speed = 3 + Math.random() * 9; // Varying speeds
			particle.velocityX = Math.cos(angle) * speed * (Math.random() > 0.5 ? 1 : -1);
			particle.velocityY = -Math.sin(angle) * speed; // Negative for upward
			// Add slight random rotation
			particle.rotationSpeed = (Math.random() - 0.5) * 0.2;
			// Longer lifespan for more dramatic effect
			particle.lifespan = 25 + Math.floor(Math.random() * 20);
			// Add update method to particles
			particle.update = function () {
				this.x += this.velocityX;
				this.y += this.velocityY;
				this.velocityY += 0.3; // Apply gravity
				this.rotation += this.rotationSpeed;
				this.lifespan--;
				// Start fading out near end of life
				if (this.lifespan < 10) {
					this.alpha -= 0.1;
				}
				if (this.lifespan <= 0 || this.alpha <= 0) {
					this.destroy();
				}
			};
		}
	};
	self.checkGroundSlamHits = function (slamHitbox) {
		// Check enemies
		for (var i = 0; i < enemies.length; i++) {
			var enemy = enemies[i];
			if (!enemy.isHit && !enemy.isDying) {
				var enemyBounds = enemy.getBounds();
				if (GameUtils.checkCollision(slamHitbox, enemyBounds)) {
					enemy.hit();
					// Play appropriate sound
					if (enemy.type === 'eyeball') {
						LK.getSound('eyeballhit').play();
					} else if (enemy.type === 'skeleton') {
						LK.getSound('skeletonhit').play();
					} else {
						LK.getSound('enemyhit').play();
					}
					// Create particles
					particleSystem.emitFromHit(enemy.x + 250, enemy.y, self.x, 5, enemy.type);
				}
			}
		}
		// Check for breakable objects (jars and chests)
		for (var i = collectibles.length - 1; i >= 0; i--) {
			var collectible = collectibles[i];
			// Only check jars and chests
			if (collectible instanceof Jar || collectible instanceof TreasureChest) {
				var itemBounds = {
					left: collectible.x - 50,
					right: collectible.x + 50,
					top: collectible.y - 75,
					bottom: collectible.y + 75
				};
				if (GameUtils.checkCollision(slamHitbox, itemBounds)) {
					collectible["break"]();
					collectibles.splice(i, 1);
				}
			}
		}
	};
	// Modified applyLandingSquash function
	self.applyLandingSquash = function () {
		// More extreme initial squash values for better visibility
		var initialScaleX = 1.2;
		var initialScaleY = 0.8;
		var targetScaleX = 1.0;
		var targetScaleY = 1.0;
		var duration = 370; // ms - slightly longer duration
		// Apply squash to the entire player container instead of individual sprites
		self.scale.set(initialScaleX, initialScaleY);
		// Clear any existing scale tweens
		tween.stop(self.scale, {
			x: true,
			y: true
		});
		// Tween back to normal scale with a slight delay
		LK.setTimeout(function () {
			tween(self.scale, {
				x: targetScaleX,
				y: targetScaleY
			}, {
				duration: duration,
				easing: tween.elasticOut // Keep the bouncier easing
			});
		}, 90); // Slightly longer delay
	};
	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 () {
		// Update timer first
		if (self.brokenPlatformTimer > 0) {
			self.brokenPlatformTimer--;
		}
		for (var i = 0; i < platforms.length; i++) {
			var platform = platforms[i];
			// Skip platforms that are broken or destroyed
			if (platform.broken || platform.destroyed) {
				continue;
			}
			// Skip this specific platform if it's the one that just broke
			if (self.brokenPlatformTimer > 0 && platform === self.lastBrokenPlatform) {
				continue;
			}
			// Keep the extra buffer for landing on platforms for a smoother experience
			// but ensure it's consistent with other checks
			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 && self.x <= platform.x + PLATFORM_HALF_WIDTH) {
				// Add this check for crumble platforms
				if (platform instanceof CrumblePlatform) {
					platform.trigger();
				}
				self.y = platform.y - PLAYER_PLATFORM_OFFSET;
				self.velocityY = 0;
				self.isJumping = false;
				self.isOnGround = true;
				self.currentPlatform = platform;
				self.applyLandingSquash();
				if (self.isOnGround) {
					self.canAirDash = true;
					self.canGroundSlam = true;
					// Clear any remaining shadow images
					self.shadowImages.forEach(function (shadow) {
						if (shadow.parent) {
							shadow.destroy();
						}
					});
					self.shadowImages = [];
				}
				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 () {
		// Add a timestamp check specifically for tutorial
		if (!self.lastJumpTime) {
			self.lastJumpTime = 0;
		}
		var currentTime = Date.now();
		if (tutorialActive && currentTime - self.lastJumpTime < 300) {
			return; // Block rapid jumps in tutorial
		}
		self.lastJumpTime = currentTime;
		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;
			// Play slide sound effect
			LK.getSound('slide').play();
			// Adjust game speed multiplier instead of base speed
			gameSpeedMultiplier = self.slideSpeedMultiplier;
		}
	};
	self.airDash = function () {
		if (!self.canAirDash || self.isOnGround || self.isSliding || self.isAttacking || self.isShooting || self.isAirDashing) {
			return;
		}
		self.isAirDashing = true;
		self.airDashTimer = self.airDashDuration;
		self.canAirDash = false;
		self.velocityY = 0;
		// Create shadow images - increased from 2 to 4 for more dramatic effect
		for (var i = 0; i < 4; i++) {
			var shadow = game.addChild(LK.getAsset('playerairdash', {
				anchorX: 0.5,
				anchorY: 0.5,
				alpha: 0.3 - i * 0.05,
				// Adjusted alpha reduction to spread across more shadows
				x: self.x - 80 * (i + 1),
				// Adjusted spacing to create a tighter trail
				y: self.y
			}));
			self.shadowImages.push(shadow);
		}
		gameSpeedMultiplier = self.airDashSpeedMultiplier;
	};
	// 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.groundSlam = function () {
		if (!self.hasGroundSlam) {
			return;
		}
		// Only allow if in air, not already slamming, and can slam
		if (!self.isOnGround && !self.isGroundSlamming && self.canGroundSlam && !self.isSliding && !self.isAttacking && !self.isAirDashing && !self.isShooting) {
			self.isGroundSlamming = true;
			self.canGroundSlam = false; // Use up the slam for this jump
			self.groundSlamFrame = 0;
			self.groundSlamPhase = "start";
			self.animationCounter = 0;
			// Boost downward velocity
			self.velocityY = 15; // Strong downward force
			// Play sound effect (can use sword slash for now)
			LK.getSound('groundsmashfalling').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);
			// Existing damage code...
			if (self.currentHealth <= 0) {
				// Save before game over
				SaveManager.save();
				// 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 (self.slideDamageTimer > 0) {
			self.slideDamageTimer--;
		}
		if (self.isAirDashing) {
			// Update air dash animation
			self.updateAirDashAnimation();
		}
		// 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
		if (!self.isAirDashing) {
			self.applyPhysics();
		}
		// Handle platform collision and falling
		self.handlePlatformCollision();
		// Handle invulnerability
		self.updateInvulnerability();
		// Update animations based on state
		if (self.isGroundSlamming) {
			self.updateGroundSlamAnimation();
		} else if (self.isAirDashing) {
			self.updateAirDashAnimation();
		} else 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 using CustomText instead of Text2
	self.text = new CustomText('+' + amount, {
		size: 70,
		fill: 0xFFFFFF
	});
	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 ShopManager = Container.expand(function () {
	var self = Container.call(this);
	self.categories = ['Health', 'Combat', 'Ranged', 'Loot', 'Spells'];
	self.currentCategory = 'Health';
	self.currentUpgradeIndex = 0; // Track current upgrade in category
	self.categoryUpgrades = []; // Store current category's upgrades
	self.selectedUpgrade = null;
	// Define upgrades with proper effects - no storage modification
	self.upgrades = [{
		id: 'health_increase',
		category: 'Health',
		title: 'Heart Container',
		description: 'Increase maximum health by one heart',
		basePrice: 300,
		maxLevel: 2,
		icon: 'health_increase_icon',
		// Effect only for immediate visual feedback in shop
		effect: function effect(level) {
			console.log("Health upgrade purchased: level " + level);
			// Could update preview if needed
		}
	}, {
		id: 'arrow_capacity',
		category: 'Ranged',
		title: 'Quiver Expansion',
		description: 'Start with more arrows',
		basePrice: 100,
		maxLevel: 5,
		icon: 'arrow_capacity_icon',
		effect: function effect(level) {
			console.log("Arrow capacity upgrade purchased: level " + level);
		}
	},
	// --- Tracking Arrow upgrade inserted here ---
	{
		id: 'tracking_arrow',
		category: 'Ranged',
		title: 'Magic Arrow',
		description: 'Arrows gain basic homing capabilities and can ricochet to nearby enemies',
		basePrice: 1200,
		maxLevel: 1,
		icon: 'arrow_finder_icon',
		effect: function effect(level) {
			console.log("Tracking Arrow upgrade purchased: level " + level);
		}
	},
	// --- End Tracking Arrow upgrade ---
	{
		id: 'levitate',
		category: 'Spells',
		title: 'Levitate',
		description: 'Coming soon.',
		basePrice: 99999,
		maxLevel: 0,
		icon: 'levitate_icon',
		effect: function effect(level) {
			console.log("Levitate upgrade purchased: level " + level);
		}
	}, {
		id: 'slide_damage',
		category: 'Combat',
		title: 'Crushing Slide',
		description: 'Sliding now damages enemies',
		basePrice: 1000,
		maxLevel: 1,
		icon: 'icon_combat',
		effect: function effect(level) {
			console.log("Slide damage upgrade purchased: level " + level);
		}
	}, {
		id: 'ground_slam',
		category: 'Combat',
		title: 'Ground Slam',
		description: 'Smash downward by swiping down from the air to damage enemies below',
		basePrice: 1000,
		maxLevel: 1,
		icon: 'icon_combat',
		effect: function effect(level) {
			console.log("Ground slam upgrade purchased: level " + level);
		}
	}, {
		id: 'better_loot',
		category: 'Loot',
		title: 'Fortune Finder',
		description: 'Increase chances of finding valuable loot',
		basePrice: 400,
		maxLevel: 4,
		icon: 'icon_treasure',
		effect: function effect(level) {}
	}, {
		id: 'potion_chance',
		category: 'Health',
		title: 'Potion Finder',
		description: 'Increases the chance of health potions dropping from breakable objects',
		basePrice: 300,
		// Cheaper than health container (600)
		maxLevel: 3,
		icon: 'healthpotion',
		effect: function effect(level) {}
	}, {
		id: 'arrow_finder',
		category: 'Ranged',
		title: 'Arrow Finder',
		description: 'Increases the chance of arrows dropping from breakable objects',
		basePrice: 300,
		maxLevel: 3,
		icon: 'arrow_capacity_icon',
		effect: function effect(level) {}
	}, {
		id: 'loot_magnet',
		category: 'Loot',
		title: 'Loot Magnet',
		description: 'Increases collection range for coins and items',
		basePrice: 300,
		maxLevel: 3,
		icon: 'icon_treasure',
		effect: function effect(level) {
			console.log("Loot magnet upgrade purchased: level " + level);
		}
	}];
	// Create tabs
	self.createTabs();
	// Create upgrade list
	self.upgradeList = new Container();
	self.addChild(self.upgradeList);
	// Create buy button
	self.createBuyButton();
	self.initPlayerAnimation();
	// Initial setup
	self.showCategory('Health');
	self.createArrows();
	return self;
});
var ShopUpgrade = Container.expand(function (config) {
	var self = Container.call(this);
	// Keep all existing properties
	self.id = config.id;
	self.category = config.category;
	self.title = config.title;
	self.description = config.description;
	self.basePrice = config.basePrice;
	self.level = 0;
	self.maxLevel = config.maxLevel || 1;
	self.icon = config.icon;
	self.effect = config.effect;
	// Create a container for the window contents
	self.windowContainer = new Container();
	self.addChild(self.windowContainer);
	// Add background to the container
	self.background = self.windowContainer.attachAsset('shop_window', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.iconSprite = self.windowContainer.attachAsset(self.id + '_icon', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.iconSprite.x = -330;
	self.iconSprite.y = -500;
	self.titleText = new CustomText(self.title, {
		size: 50,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.windowContainer.addChild(self.titleText);
	self.titleText.x = -200;
	self.titleText.y = -500;
	self.descriptionText = new CustomText(self.description + '.', {
		size: 45,
		fill: 0xA89F8A,
		letterSpacing: 5,
		wordWrapWidth: 800,
		wordWrap: true
	});
	self.windowContainer.addChild(self.descriptionText);
	self.descriptionText.x = -370;
	self.descriptionText.y = -350;
	self.levelText = new CustomText('LEVEL ' + self.level + (self.level >= self.maxLevel ? ' MAX' : ''), {
		size: 60,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.windowContainer.addChild(self.levelText);
	self.levelText.x = -370;
	self.levelText.y = 530;
	// Methods with proper separation of concerns
	self.getCurrentPrice = function () {
		return self.basePrice * Math.pow(2, self.level);
	};
	self.canPurchase = function (gold) {
		return self.level < self.maxLevel && gold >= self.getCurrentPrice();
	};
	self.purchase = function () {
		if (self.level < self.maxLevel) {
			this.level++;
			// UPDATE THE REGISTRY
			switch (this.id) {
				case 'health_increase':
					UPGRADE_REGISTRY.health_increase = this.level;
					break;
				case 'arrow_capacity':
					UPGRADE_REGISTRY.arrow_capacity = this.level;
					break;
				case 'levitate':
					UPGRADE_REGISTRY.levitate = this.level;
					break;
				case 'slide_damage':
					UPGRADE_REGISTRY.slide_damage = this.level;
					break;
				case 'better_loot':
					UPGRADE_REGISTRY.better_loot = this.level;
					break;
				case 'potion_chance':
					UPGRADE_REGISTRY.potion_chance = this.level;
					break;
				case 'arrow_finder':
					UPGRADE_REGISTRY.arrow_finder = this.level;
					break;
				case 'ground_slam':
					UPGRADE_REGISTRY.ground_slam = this.level;
					break;
				case 'loot_magnet':
					UPGRADE_REGISTRY.loot_magnet = this.level;
					break;
				case 'tracking_arrow':
					UPGRADE_REGISTRY.tracking_arrow = this.level;
					break;
			}
			// Update UI text
			this.levelText.setText('LEVEL ' + this.level + (this.level >= this.maxLevel ? ' MAX' : ''));
			// Apply the effect for immediate feedback
			this.effect(this.level);
			return true;
		}
		return false;
	};
});
// Add ShopUpgrade.prototype methods here
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 with default values
	self.speed = 6;
	self.rotationSpeed = -0.1;
	self.constantVelocity = 8; // Can be overridden when created
	// Update method called every frame
	self.update = function () {
		// Use constantVelocity if set, otherwise fall back to speed * gameSpeedMultiplier
		var velocity = self.constantVelocity || self.speed * gameSpeedMultiplier;
		// Move the sword
		self.x -= velocity;
		// 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;
});
var Spore = Container.expand(function () {
	var self = Container.call(this);
	// Animation properties
	self.frame = 0;
	self.animationCounter = 0;
	self.animationSpeed = 0.035;
	self.sprites = [];
	// Physics properties
	self.velocityX = 0;
	self.velocityY = 0;
	self.lifespan = 180; // Increased lifespan
	self.initialSpeed = 4.0; // Slower initial speed
	self.speed = self.initialSpeed;
	self.damage = 1;
	self.isExploding = false;
	self.hasHitPlayer = false;
	self.isActive = true; // New flag to track if this spore is still active for collision
	// Initialize animation sprites
	for (var i = 1; i <= 8; i++) {
		var sprite = self.attachAsset('spore' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 1.3,
			scaleY: 1.3
		});
		sprite.alpha = i === 1 ? 1 : 0;
		self.sprites.push(sprite);
	}
	// Get collision bounds - only return bounds if the spore is active
	self.getBounds = function () {
		if (!self.isActive) {
			// Return a dummy bounds that can't possibly collide with anything
			return {
				left: -9999,
				right: -9999,
				top: -9999,
				bottom: -9999
			};
		}
		return {
			left: self.x - 40,
			right: self.x + 40,
			top: self.y - 40,
			bottom: self.y + 40
		};
	};
	// Override the destroy method to ensure proper cleanup
	var originalDestroy = self.destroy;
	self.destroy = function () {
		self.isActive = false; // Mark as inactive first
		// Remove from global spores array if it exists
		if (window.spores) {
			var index = window.spores.indexOf(self);
			if (index !== -1) {
				window.spores.splice(index, 1);
			}
		}
		// Call the original destroy method
		originalDestroy.call(self);
	};
	// Hide all sprites
	self.hideAllSprites = function () {
		for (var i = 0; i < self.sprites.length; i++) {
			self.sprites[i].alpha = 0;
		}
	};
	// Start explosion animation
	self.explode = function () {
		if (!self.isExploding) {
			self.isExploding = true;
			self.frame = 4; // Start at explosion frame
			self.lifespan = Math.min(self.lifespan, 30); // Short explosion time
			// Stop movement
			self.velocityX *= 0.3;
			self.velocityY *= 0.3;
			// Deactivate the spore for collision once explosion starts
			self.isActive = false;
		}
	};
	// Update method
	self.update = function () {
		// Hide all sprites first
		self.hideAllSprites();
		// Move spore
		self.x += self.velocityX;
		self.y += self.velocityY;
		// Apply platform movement to match world movement
		self.x -= PLATFORM_SPEED * gameSpeedMultiplier;
		// Update animation
		self.animationCounter += self.animationSpeed;
		if (self.animationCounter >= 1) {
			self.animationCounter = 0;
			self.frame = (self.frame + 1) % self.sprites.length;
		}
		self.sprites[self.frame].alpha = 1;
		// Decrease lifespan
		self.lifespan--;
		// Start fading out near end of life
		if (self.lifespan < 30) {
			self.alpha = self.lifespan / 30;
		}
		// Destroy if off screen or lifespan ended
		if (self.x < -100 || self.x > GAME_WIDTH + 100 || self.y < -100 || self.y > GAME_HEIGHT + 100 || self.lifespan <= 0) {
			self.destroy();
		}
	};
	return self;
});
/**** 
* Constants
****/ 
var Torch = Container.expand(function () {
	var self = Container.call(this);
	// Create base torch sprite
	self.base = self.attachAsset('torch', {
		anchorX: 0.5,
		anchorY: 1
	});
	// Create flame sprite
	self.flame = self.attachAsset('torchflame', {
		anchorX: 0.5,
		anchorY: 1,
		y: -180
	});
	// Create aura sprite
	self.aura = self.attachAsset('torchaura', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0.3,
		y: -250
	});
	// Animation properties
	self.flameTime = Math.random() * Math.PI * 2;
	self.auraTime = Math.random() * Math.PI * 2;
	self.flameSpeed = 0.05;
	self.auraSpeed = 0.03;
	// Update animation
	self.update = function () {
		// Animate flame scale
		self.flameTime += self.flameSpeed;
		var flameScale = 1 + Math.sin(self.flameTime) * 0.2;
		self.flame.scaleY = flameScale;
		// Random flip chance for flame
		if (Math.random() < 0.02) {
			self.flame.scaleX *= -1;
		}
		// Animate aura alpha
		self.auraTime += self.auraSpeed;
		var auraAlpha = 0.3 + Math.sin(self.auraTime) * 0.15;
		self.aura.alpha = auraAlpha;
	};
	return self;
});
var TreasureChest = Container.expand(function () {
	var self = Container.call(this);
	// Attach chest sprite
	self.sprite = self.attachAsset('treasurechest', {
		anchorX: 0.5,
		anchorY: 0.5,
		tint: 0xC0C0C0
	});
	// Initialize as breakable
	BreakableBehavior.initBreakable(self);
	// Break functionality
	self["break"] = function () {
		BreakableBehavior.standardBreak(self, TreasureChestPiece, 4, function (chest) {
			// Get loot multiplier from player
			var lootMultiplier = player.lootMultiplier || 1.0;
			// Spawn health potion with medium chance - improved by loot multiplier
			if (Math.random() < 0.25 * (player.potionDropChance || 1.0)) {
				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 - improved by loot multiplier
			if (Math.random() < 0.40 * (player.arrowDropChance || 1.0)) {
				var arrowCount = Math.floor(GameUtils.randomRange(1, 4) * lootMultiplier);
				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 - quantity affected by loot multiplier
			var totalItems = Math.floor(GameUtils.randomRange(3, 9) * lootMultiplier);
			for (var i = 0; i < totalItems; i++) {
				// Random chance for different gems - probabilities improved by loot multiplier
				var rand = Math.random();
				var item;
				// Increase chances for better gems based on multiplier (cap at reasonable values)
				var diamondChance = Math.min(0.05 * lootMultiplier, 0.15);
				var emeraldChance = Math.min(0.15 * lootMultiplier, 0.25);
				var rubyChance = Math.min(0.30 * lootMultiplier, 0.40);
				if (rand < diamondChance) {
					item = new Coin('diamond');
				} else if (rand < emeraldChance) {
					item = new Coin('emerald');
				} else if (rand < rubyChance) {
					item = new Coin('ruby');
				} else {
					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',
		AIR_DASH: 'air_dash',
		COMPLETE: 'complete'
	}; //POG_REPLACE_LINE>{yq}
	// 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.hasAirDashSucceeded = false;
	self.firstJumpPerformed = false;
	self.secondJumpPerformed = false;
	// Message display
	self.messageContainer = new Container();
	self.messageText = new CustomText('', {
		size: 60,
		fill: 0xFFFFFF,
		letterSpacing: 5,
		wordWrap: true,
		wordWrapWidth: 1200,
		anchorX: 0.5
	});
	self.messageContainer.addChild(self.messageText);
	self.messageContainer.width = 1200;
	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") {
				// Add visual feedback animation
				tween(self.backButton, {
					scaleX: 0.9,
					scaleY: 0.9
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(self.backButton, {
							scaleX: 1,
							scaleY: 1
						}, {
							duration: 200,
							easing: tween.easeOut,
							onFinish: function onFinish() {
								self.complete();
							}
						});
					}
				});
				// Play menu select sound
				LK.getSound('menuselect').play();
			}
		};
		self.backButton.down = self.backButtonDown;
		// Clear any existing enemies/arrows
		enemies = [];
		arrows = [];
		self.spawnTutorialEnemy();
	};
	// Message handling
	self.setMessage = function (text) {
		// First, remove the existing text
		if (self.messageText.parent) {
			self.messageContainer.removeChild(self.messageText);
		}
		// Create a new text object each time (like the shop does)
		self.messageText = new CustomText(text, {
			size: 60,
			fill: 0xFFFFFF,
			letterSpacing: 5,
			wordWrap: true,
			wordWrapWidth: 1200,
			anchorX: 0.5
		});
		// Add to container with fixed positioning
		self.messageContainer.addChild(self.messageText);
		self.messageText.x = 0;
		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;
				}
				if (self.hasSlideSucceeded && !player.isSliding) {
					self.currentState = self.states.AIR_DASH;
					self.setMessage("Swipe right while in the air to air dash!");
					self.hasSlideSucceeded = false;
					self.hasAirDashSucceeded = false;
					// Reset player state for air dashing
					player.canAirDash = true;
				}
				break;
			case self.states.AIR_DASH:
				// Simply check if the player has air dashed
				if (!self.hasAirDashSucceeded && player.isAirDashing) {
					self.hasAirDashSucceeded = true;
					// As soon as they air dash, consider the tutorial complete
					LK.setTimeout(function () {
						self.currentState = self.states.COMPLETE;
						self.setMessage("That's it!\nNow go get some treasure!");
						LK.setTimeout(function () {
							self.complete();
						}, 3000);
					}, 1000); // Wait 1 second after air dash before showing completion message
				}
				break;
		}
	};
	// Input handling
	// Inside TutorialManager's checkInput method, where it checks different states
	self.checkInput = function (startX, startY, endX, endY) {
		if (!gameStarted || player.isDying) {
			return;
		}
		// Initialize the jump tracking property if it doesn't exist
		if (!self.inputProcessed) {
			self.inputProcessed = false;
		}
		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();
			}
			self.inputProcessed = true;
			return;
		}
		// Don't process the same input multiple times
		if (self.inputProcessed) {
			return;
		}
		self.inputProcessed = true;
		// Track last jump time to prevent multiple jumps from a single swipe
		if (!self.lastJumpTime) {
			self.lastJumpTime = 0;
		}
		var currentTime = Date.now();
		// 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();
			}
			// ADD THIS AIR DASH HANDLING RIGHT HERE
			else if (deltaX > 70 && self.currentState === self.states.AIR_DASH && !player.isOnGround) {
				player.airDash();
			}
		} else {
			if (deltaY < -120) {
				// Upward swipe with cooldown check
				if (currentTime - self.lastJumpTime > JUMP_COOLDOWN) {
					if (self.currentState === self.states.JUMP) {
						self.hasJumped = true;
						player.jump();
						self.lastJumpTime = currentTime;
					} else if (self.currentState === self.states.DOUBLE_JUMP) {
						self.hasDoubleJumped = true;
						player.jump();
						self.lastJumpTime = currentTime;
					} else if (self.currentState === self.states.AIR_DASH) {
						// Allow jumping during air dash tutorial too
						player.jump();
						self.lastJumpTime = currentTime;
					}
				}
			} else if (deltaY > 120 && 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
****/ 
//resetAllStorage();
// Add ShopUpgrade.prototype methods here
// Base collectible behavior for items that can be collected
/**** 
* Game Management
****/ 
initializeStorage();
ShopUpgrade.prototype.setSelected = function (isSelected) {
	var self = this;
	// Find the background asset within windowContainer children
	var background = null;
	for (var i = 0; i < self.windowContainer.children.length; i++) {
		if (self.windowContainer.children[i].asset && (self.windowContainer.children[i].asset.id === 'shop_upgrade_bg' || self.windowContainer.children[i].asset.id === 'shop_upgrade_bg_selected')) {
			background = self.windowContainer.children[i];
			break;
		}
	}
	if (background) {
		background.asset = isSelected ? 'shop_upgrade_bg_selected' : 'shop_upgrade_bg';
	}
};
var SaveManager = {
	// Save all game data
	save: function save() {
		// Update gold in registry from score manager
		UPGRADE_REGISTRY.gold = Number(scoreManager.getScore()) || 0;
		// Save gold from registry to storage
		storage.gold = UPGRADE_REGISTRY.gold;
		// Save all upgrade levels directly from registry
		storage.healthUpgrade = UPGRADE_REGISTRY.health_increase;
		storage.arrowUpgrade = UPGRADE_REGISTRY.arrow_capacity;
		storage.levitateUpgrade = UPGRADE_REGISTRY.levitate;
		storage.slideUpgrade = UPGRADE_REGISTRY.slide_damage;
		storage.lootUpgrade = UPGRADE_REGISTRY.better_loot;
		storage.potionChanceUpgrade = UPGRADE_REGISTRY.potion_chance;
		storage.arrowFinderUpgrade = UPGRADE_REGISTRY.arrow_finder;
		storage.groundSlamUpgrade = UPGRADE_REGISTRY.ground_slam;
		storage.lootMagnetUpgrade = UPGRADE_REGISTRY.loot_magnet;
		// Save tracking_arrow upgrade
		storage.trackingArrowUpgrade = UPGRADE_REGISTRY.tracking_arrow;
		// Track timestamp
		storage.lastSaveTime = Date.now();
	},
	// Load saved game data
	load: function load() {
		// Initialize registry from storage with safe number conversions
		UPGRADE_REGISTRY.health_increase = parseInt(storage.healthUpgrade) || 0;
		UPGRADE_REGISTRY.arrow_capacity = parseInt(storage.arrowUpgrade) || 0;
		UPGRADE_REGISTRY.levitate = parseInt(storage.levitateUpgrade) || 0;
		UPGRADE_REGISTRY.slide_damage = parseInt(storage.slideUpgrade) || 0;
		UPGRADE_REGISTRY.better_loot = parseInt(storage.lootUpgrade) || 0;
		UPGRADE_REGISTRY.potion_chance = parseInt(storage.potionChanceUpgrade) || 0;
		UPGRADE_REGISTRY.arrow_finder = parseInt(storage.arrowFinderUpgrade) || 0;
		UPGRADE_REGISTRY.ground_slam = parseInt(storage.groundSlamUpgrade) || 0;
		UPGRADE_REGISTRY.loot_magnet = parseInt(storage.lootMagnetUpgrade) || 0;
		// Load tracking_arrow upgrade
		UPGRADE_REGISTRY.tracking_arrow = parseInt(storage.trackingArrowUpgrade) || 0;
		// Load gold with explicit number conversion
		UPGRADE_REGISTRY.gold = parseInt(storage.gold) || 0;
		// Update score display
		if (scoreManager) {
			scoreManager.setScore(UPGRADE_REGISTRY.gold);
		}
		// If shop exists, update the visual state of upgrades
		this.updateShopDisplay();
		// Apply the loaded upgrade effects to the player
		this.applyUpgradeEffects();
	},
	// Update shop display based on registry values
	updateShopDisplay: function updateShopDisplay() {
		if (game.shopManager && game.shopManager.upgrades) {
			game.shopManager.upgrades.forEach(function (upgrade) {
				// Get the appropriate level from registry
				var registryLevel = 0;
				switch (upgrade.id) {
					case 'health_increase':
						registryLevel = UPGRADE_REGISTRY.health_increase;
						break;
					case 'arrow_capacity':
						registryLevel = UPGRADE_REGISTRY.arrow_capacity;
						break;
					case 'levitate':
						registryLevel = UPGRADE_REGISTRY.levitate;
						break;
					case 'slide_damage':
						registryLevel = UPGRADE_REGISTRY.slide_damage;
						break;
					case 'better_loot':
						registryLevel = UPGRADE_REGISTRY.better_loot;
						break;
					case 'potion_chance':
						registryLevel = UPGRADE_REGISTRY.potion_chance;
						break;
					case 'arrow_finder':
						registryLevel = UPGRADE_REGISTRY.arrow_finder;
						break;
					case 'ground_slam':
						registryLevel = UPGRADE_REGISTRY.ground_slam;
						break;
					case 'loot_magnet':
						registryLevel = UPGRADE_REGISTRY.loot_magnet;
						break;
					case 'tracking_arrow':
						registryLevel = UPGRADE_REGISTRY.tracking_arrow;
						break;
				}
				// Update internal level directly from registry
				upgrade.level = registryLevel;
				// Update UI text
				if (upgrade.levelText) {
					upgrade.levelText.setText('LEVEL ' + upgrade.level + (upgrade.level >= upgrade.maxLevel ? ' MAX' : ''));
				}
				console.log("Updated " + upgrade.id + " display to level " + upgrade.level);
			});
		}
	},
	// Apply upgrade effects to player
	applyUpgradeEffects: function applyUpgradeEffects(player) {
		if (!player && window.player) {
			player = window.player;
		}
		if (!player) {
			return;
		}
		// Reset to base values
		player.maxHealth = 3;
		player.ammoCount = 5;
		player.hasLevitate = false;
		player.slideDamage = false;
		player.hasGroundSlam = false;
		player.arrowDropChance = 1.0;
		player.lootMultiplier = 1.0;
		player.potionDropChance = 1.0;
		player.lootMagnetRange = 1.0;
		// Apply all upgrades from registry
		player.maxHealth += UPGRADE_REGISTRY.health_increase;
		player.currentHealth = player.maxHealth;
		player.ammoCount += UPGRADE_REGISTRY.arrow_capacity * 2;
		player.hasLevitate = UPGRADE_REGISTRY.levitate > 0;
		player.hasGroundSlam = UPGRADE_REGISTRY.ground_slam > 0;
		player.slideDamage = UPGRADE_REGISTRY.slide_damage > 0;
		player.lootMultiplier = 1 + UPGRADE_REGISTRY.better_loot * 0.15;
		player.potionDropChance = 1 + UPGRADE_REGISTRY.potion_chance * 0.5;
		player.arrowDropChance = 1 + UPGRADE_REGISTRY.arrow_finder * 0.5;
		player.lootMagnetRange = 1 + UPGRADE_REGISTRY.loot_magnet * 0.5;
		// Update UI elements
		if (player.heartContainer) {
			player.heartContainer.updateMaxHealth(player.maxHealth);
			player.heartContainer.updateHealth(player.currentHealth);
		}
		if (scoreManager) {
			scoreManager.updateAmmo(player.ammoCount);
		}
	},
	// Clear all saved data
	clear: function clear() {
		// Reset storage
		storage.gold = 0;
		storage.healthUpgrade = 0;
		storage.arrowUpgrade = 0;
		storage.levitateUpgrade = 0;
		storage.slideUpgrade = 0;
		storage.lootUpgrade = 0;
		// Reset registry
		UPGRADE_REGISTRY.gold = 0;
		UPGRADE_REGISTRY.health_increase = 0;
		UPGRADE_REGISTRY.arrow_capacity = 0;
		UPGRADE_REGISTRY.levitate = 0;
		UPGRADE_REGISTRY.slide_damage = 0;
		UPGRADE_REGISTRY.better_loot = 0;
		UPGRADE_REGISTRY.potion_chance = 0;
		UPGRADE_REGISTRY.arrow_finder = 0;
	}
};
ShopManager.prototype.initPlayerAnimation = function () {
	var self = this;
	self.playerSprites = [];
	self.idleFrame = 0;
	self.animationCounter = 0;
	self.animationSpeed = 0.037;
	// Create sprites for idle animation
	for (var i = 1; i <= 4; i++) {
		// Create and attach first
		var sprite = self.attachAsset('playeridle' + i, {
			anchorX: 0.5,
			anchorY: 0.5,
			x: -550,
			y: 400
		});
		// Set initial visibility
		sprite.alpha = i === 1 ? 1 : 0;
		// Push to array after attaching
		self.playerSprites.push(sprite);
	}
};
ShopManager.prototype.hideAllSprites = function () {
	var self = this;
	for (var i = 0; i < self.playerSprites.length; i++) {
		self.playerSprites[i].alpha = 0;
	}
};
ShopManager.prototype.updatePlayerAnimation = function () {
	var self = this;
	// Hide all sprites first
	self.hideAllSprites();
	// Update animation
	self.animationCounter += self.animationSpeed;
	if (self.animationCounter >= 1) {
		self.animationCounter = 0;
		self.idleFrame = (self.idleFrame + 1) % self.playerSprites.length;
	}
	// Show current frame
	self.playerSprites[self.idleFrame].alpha = 1;
};
ShopManager.prototype.updateArrowVisibility = function () {
	var self = this;
	// Check if arrows exist before updating their visibility
	if (self.leftArrow) {
		// Only show left arrow if we're not at the first upgrade
		self.leftArrow.alpha = self.currentUpgradeIndex > 0 ? 1 : 0;
	}
	if (self.rightArrow) {
		// Only show right arrow if we're not at the last upgrade
		self.rightArrow.alpha = self.currentUpgradeIndex < self.categoryUpgrades.length - 1 ? 1 : 0;
	}
};
ShopManager.prototype.createTabs = function () {
	var self = this;
	// Add shop background first
	self.shopBackground = self.attachAsset('shop_background', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Add shop banner
	self.shopBanner = self.attachAsset('shop_banner', {
		anchorX: 0.5,
		anchorY: 0.5,
		y: -1000
	});
	// Create tabs container AFTER background
	self.tabs = new Container();
	self.tabs.x = 0;
	self.tabs.y = -880;
	self.addChild(self.tabs); // This adds it on top of previously added elements
	// Add windows
	self.playerWindow = self.attachAsset('shop_player', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: -490,
		y: 60
	});
	// Gold display
	self.goldWindow = new Container(); // Create container instead of asset
	// Add dark window background first
	var goldWindowBg = self.goldWindow.attachAsset('shop_dark_window', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.goldWindow.x = -490;
	self.goldWindow.y = 850;
	self.addChild(self.goldWindow);
	self.createGoldDisplay();
	// Create category tabs
	var spacing = 335;
	var startX = -(self.categories.length - 1) * spacing / 2;
	self.categories.forEach(function (category, index) {
		var tab = new Container();
		// Create both tab backgrounds
		var normalBg = tab.attachAsset('shop_tab', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		var selectedBg = tab.attachAsset('shop_tab_selected', {
			anchorX: 0.5,
			anchorY: 0.5
		});
		selectedBg.alpha = 0; // Initially hide the selected state
		var text = new CustomText(category, {
			size: 48,
			fill: 0xE6D5AC,
			letterSpacing: 5
		});
		tab.addChild(text);
		// Position text at center of tab
		text.x = -text.width / 2 + 10;
		text.y = 0;
		tab.x = startX + spacing * index;
		tab.y = 180;
		tab.down = function () {
			// Play sound when tab is clicked
			LK.getSound('menuselect').play();
			self.showCategory(category);
		};
		self.tabs.addChild(tab);
	});
};
ShopManager.prototype.createBuyButton = function () {
	var self = this;
	self.buyButton = new Container();
	self.buyButtonBg = self.buyButton.attachAsset('shop_button', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.priceContainer = new Container();
	self.priceText = new CustomText('', {
		size: 60,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	self.priceCoinIcon = LK.getAsset('coin', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3
	});
	// Position coin at fixed location
	self.priceCoinIcon.x = 160;
	self.priceCoinIcon.y = 40;
	// Initially position text with some default value
	self.priceText.x = self.priceCoinIcon.x - 10;
	self.priceText.y = 40;
	self.priceContainer.addChild(self.priceText);
	self.priceContainer.addChild(self.priceCoinIcon);
	self.priceContainer.y = -300;
	self.buyButton.addChild(self.priceContainer);
	self.buyButton.x = 525;
	self.buyButton.y = 850;
	self.buyButton.down = function () {
		self.purchaseSelectedUpgrade();
	};
	self.addChild(self.buyButton);
};
ShopManager.prototype.showCategory = function (category) {
	var self = this;
	self.currentCategory = category;
	// Filter upgrades for this category
	self.categoryUpgrades = self.upgrades.filter(function (upgrade) {
		return upgrade.category === category;
	});
	// Reset to first upgrade in category
	self.currentUpgradeIndex = 0;
	// Show first upgrade
	self.showUpgradeAtIndex(0);
	// Update tab visuals
	self.tabs.children.forEach(function (tab) {
		var isSelected = tab.children[2].text === category;
		tab.children[0].alpha = isSelected ? 0 : 1; // normal bg
		tab.children[1].alpha = isSelected ? 1 : 0; // selected bg
	});
};
ShopManager.prototype.showUpgradeAtIndex = function (index) {
	var self = this;
	// Update current index
	self.currentUpgradeIndex = index;
	// Clear existing upgrades
	self.upgradeList.removeChildren();
	// Show upgrade at current index
	if (self.categoryUpgrades.length > 0) {
		var upgradeConfig = self.categoryUpgrades[self.currentUpgradeIndex];
		var upgrade = new ShopUpgrade(upgradeConfig);
		// Initialize level from registry based on ID
		switch (upgrade.id) {
			case 'health_increase':
				upgrade.level = UPGRADE_REGISTRY.health_increase;
				break;
			case 'arrow_capacity':
				upgrade.level = UPGRADE_REGISTRY.arrow_capacity;
				break;
			case 'levitate':
				upgrade.level = UPGRADE_REGISTRY.levitate;
				break;
			case 'slide_damage':
				upgrade.level = UPGRADE_REGISTRY.slide_damage;
				break;
			case 'better_loot':
				upgrade.level = UPGRADE_REGISTRY.better_loot;
				break;
			case 'potion_chance':
				upgrade.level = UPGRADE_REGISTRY.potion_chance || 0;
				break;
			case 'arrow_finder':
				upgrade.level = UPGRADE_REGISTRY.arrow_finder || 0;
				break;
			case 'ground_slam':
				upgrade.level = UPGRADE_REGISTRY.ground_slam;
				break;
			case 'loot_magnet':
				upgrade.level = UPGRADE_REGISTRY.loot_magnet;
				break;
			case 'tracking_arrow':
				upgrade.level = UPGRADE_REGISTRY.tracking_arrow || 0;
				break;
		}
		// Update level text to match registry
		upgrade.levelText.setText('LEVEL ' + upgrade.level + (upgrade.level >= upgrade.maxLevel ? ' MAX' : ''));
		upgrade.x = 355;
		upgrade.y = 60;
		self.upgradeList.addChild(upgrade);
		// Make sure we're selecting the upgrade
		self.selectedUpgrade = upgrade;
	}
	// Update arrow visibility
	self.updateArrowVisibility();
	// Update buy button
	self.updateBuyButton();
};
ShopManager.prototype.createArrows = function () {
	var self = this;
	// Create left arrow
	self.leftArrow = new Container();
	self.leftArrow.attachAsset('shop_arrow', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: -1,
		// Flip horizontally
		alpha: 1
	});
	self.leftArrow.x = 0;
	self.leftArrow.y = 150; // Center with the upgrade display
	self.addChild(self.leftArrow);
	// Create right arrow
	self.rightArrow = new Container();
	self.rightArrow.attachAsset('shop_arrow', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 1
	});
	self.rightArrow.x = 710;
	self.rightArrow.y = 150; // Center with the upgrade display
	self.addChild(self.rightArrow);
	// Set up interactions
	self.leftArrow.down = function () {
		if (self.currentUpgradeIndex > 0) {
			LK.getSound('menuselect').play();
			self.showUpgradeAtIndex(self.currentUpgradeIndex - 1);
		}
	};
	self.rightArrow.down = function () {
		if (self.currentUpgradeIndex < self.categoryUpgrades.length - 1) {
			LK.getSound('menuselect').play();
			self.showUpgradeAtIndex(self.currentUpgradeIndex + 1);
		}
	};
	// Set initial visibility
	if (self.leftArrow && self.rightArrow) {
		self.updateArrowVisibility();
	}
};
ShopManager.prototype.selectUpgrade = function (upgrade) {
	var self = this;
	// Deselect previous upgrade
	if (self.selectedUpgrade) {
		self.selectedUpgrade.setSelected(false);
	}
	self.selectedUpgrade = upgrade;
	upgrade.setSelected(true);
	// Update buy button and price
	self.updateBuyButton();
};
ShopManager.prototype.updateBuyButton = function () {
	var self = this;
	if (!self.selectedUpgrade) {
		return;
	}
	var price = self.selectedUpgrade.getCurrentPrice();
	var canAfford = scoreManager.getScore() >= price;
	var isMaxLevel = self.selectedUpgrade.level >= self.selectedUpgrade.maxLevel;
	// Store current price length to detect changes
	var oldLength = self.priceText.text.length;
	var newLength = (isMaxLevel ? 'MAX' : price.toString()).length;
	// Store current vertical position
	var currentY = self.priceText.y;
	// Update buy button based on max level first
	if (isMaxLevel) {
		self.priceText.setText('MAX');
		self.priceCoinIcon.visible = false;
		self.priceText.x = 0; // Center the MAX text
		self.buyButtonBg.tint = 0x888888;
	} else {
		// If changing from 4 to 3 digits, completely recreate the text
		if (oldLength > 3 && newLength === 3) {
			// Remove the old text
			if (self.priceText.parent) {
				self.priceContainer.removeChild(self.priceText);
			}
			// Create new text with same properties
			self.priceText = new CustomText(price.toString(), {
				size: 60,
				fill: 0xFFFFFF,
				letterSpacing: 5
			});
			// Restore the vertical position
			self.priceText.y = currentY;
			self.priceContainer.addChild(self.priceText);
		} else {
			// Just update the text for other cases
			self.priceText.setText(price.toString());
		}
		self.priceCoinIcon.visible = true;
		self.priceCoinIcon.x = 160; // Fixed position
		var textPadding = 10;
		self.priceText.x = self.priceCoinIcon.x - textPadding - self.priceText.width;
		self.buyButtonBg.tint = canAfford ? 0xFFFFFF : 0x888888;
	}
	// Update total gold display
	self.updateTotalGold();
};
ShopManager.prototype.purchaseSelectedUpgrade = function () {
	var self = this;
	if (!self.selectedUpgrade) {
		return;
	}
	var price = self.selectedUpgrade.getCurrentPrice();
	if (scoreManager.getScore() >= price && self.selectedUpgrade.level < self.selectedUpgrade.maxLevel) {
		// Add visual feedback animation for buy button
		tween(self.buyButton, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(self.buyButton, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut
				});
			}
		});
		// Deduct price using the global scoreManager
		scoreManager.addScore(-price);
		// Call purchase() method
		var result = self.selectedUpgrade.purchase();
		// Play purchase sound
		LK.getSound('shopbuy').play();
		// Update button UI
		self.updateBuyButton();
		// Save the changes
		SaveManager.save();
	} else {
		// Play "can't buy" sound when player doesn't have enough gold or upgrade is maxed
		LK.getSound('cantbuy').play();
	}
};
ShopManager.prototype.createGoldDisplay = function () {
	var self = this;
	// Create a container for the gold display
	self.goldDisplayContainer = new Container();
	self.totalGoldText = new CustomText('0', {
		size: 90,
		fill: 0xE6D5AC,
		letterSpacing: 5
	});
	self.totalGoldCoinIcon = LK.getAsset('coin', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	self.goldDisplayContainer.addChild(self.totalGoldText);
	self.goldDisplayContainer.addChild(self.totalGoldCoinIcon);
	self.goldDisplayContainer.x = 90; // Center of gold window
	self.goldDisplayContainer.y = -50;
	// Position text and coin
	self.totalGoldText.x = -50;
	self.totalGoldText.y = 65;
	self.totalGoldCoinIcon.x = 70;
	self.totalGoldCoinIcon.y = 70;
	// Add to the window
	self.goldWindow.addChild(self.goldDisplayContainer);
	// Initial update
	self.updateTotalGold();
};
ShopManager.prototype.updateTotalGold = function () {
	var self = this;
	var totalGold = scoreManager.getScore();
	self.totalGoldText.setText(totalGold.toString());
	// Keep coin at fixed position
	self.totalGoldCoinIcon.x = 165; // Fixed position
	// Right-align text relative to coin
	// Adjust position based on digit count but keep right edge aligned
	var textPadding = 50; // Space between text and coin
	self.totalGoldText.x = self.totalGoldCoinIcon.x - textPadding - self.totalGoldText.width;
};
ShopManager.prototype.update = function () {
	this.updatePlayerAnimation();
	this.updateBuyButton();
};
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;
		}
		// Only apply magnet effect if it's not a health potion OR if player is not at full health
		if (!(self instanceof HealthPotion && player.currentHealth >= player.maxHealth)) {
			if (player.lootMagnetRange > 1) {
				var dx = player.x - self.x;
				var dy = player.y - self.y;
				var distance = Math.sqrt(dx * dx + dy * dy);
				var maxAttractionRange = 300 * player.lootMagnetRange;
				if (distance < maxAttractionRange) {
					// Calculate attraction strength based on distance
					var strength = (1 - distance / maxAttractionRange) * 3;
					self.velocityX += dx / distance * strength;
					self.velocityY += dy / distance * strength;
				}
			}
		}
		// 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 magnetRange = player.lootMagnetRange || 1;
		var itemBounds = {
			left: self.x - 25 * magnetRange,
			right: self.x + 25 * magnetRange,
			top: self.y - 25 * magnetRange,
			bottom: self.y + 25 * magnetRange
		};
		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;
var RICOCHET_SEARCH_RADIUS_SQ = 1000 * 1000; // Further increased search radius for ricochet targets
var RICOCHET_ARROW_SPEED = 40;
var UPGRADE_REGISTRY = {
	gold: 0,
	health_increase: 0,
	arrow_capacity: 0,
	levitate: 0,
	slide_damage: 0,
	better_loot: 0,
	potion_chance: 0,
	arrow_finder: 0,
	ground_slam: 0,
	loot_magnet: 0,
	tracking_arrow: 0
};
/**** 
* Utilities
****/ 
function initializeStorage() {
	if (storage.gold === undefined) {
		storage.gold = 0;
	}
	if (storage.healthUpgrade === undefined) {
		storage.healthUpgrade = 0;
	}
	if (storage.arrowUpgrade === undefined) {
		storage.arrowUpgrade = 0;
	}
	if (storage.levitateUpgrade === undefined) {
		storage.levitateUpgrade = 0;
	}
	if (storage.slideUpgrade === undefined) {
		storage.slideUpgrade = 0;
	}
	if (storage.lootUpgrade === undefined) {
		storage.lootUpgrade = 0;
	}
	if (storage.potionChanceUpgrade === undefined) {
		storage.potionChanceUpgrade = 0;
	}
	if (storage.arrowFinderUpgrade === undefined) {
		storage.arrowFinderUpgrade = 0;
	}
	if (storage.groundSlamUpgrade === undefined) {
		storage.groundSlamUpgrade = 0;
	}
	if (storage.lootMagnetUpgrade === undefined) {
		storage.lootMagnetUpgrade = 0;
	}
	// Add trackingArrowUpgrade initialization
	if (storage.trackingArrowUpgrade === undefined) {
		storage.trackingArrowUpgrade = 0;
	}
}
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 first
	self.score = 0;
	// Define methods before using them
	self.getScore = function () {
		return self.score;
	};
	self.setScore = function (newScore) {
		var oldScore = self.score;
		self.score = newScore;
		// Update the registry
		UPGRADE_REGISTRY.gold = self.score;
		// Check if digit count has changed
		var oldLength = oldScore.toString().length;
		var newLength = newScore.toString().length;
		if (oldLength !== newLength && self.scoreText) {
			// Store current position
			var currentX = self.scoreText.x;
			var currentY = self.scoreText.y;
			// Remove old text
			if (self.scoreText.parent) {
				self.container.removeChild(self.scoreText);
			}
			// Create new text
			self.scoreText = new CustomText(newScore.toString(), {
				size: 100,
				fill: 0xFFFFFF,
				letterSpacing: 5
			});
			// Restore position properties
			self.scoreText.y = currentY;
			self.container.addChild(self.scoreText);
		} else if (self.scoreText) {
			// Just update text content
			self.scoreText.setText(newScore.toString());
		}
		// Keep right edge at a fixed position
		if (self.scoreText) {
			var rightEdgePosition = -10; // Fixed right edge position
			self.scoreText.x = rightEdgePosition - self.scoreText.width;
		}
	};
	self.addScore = function (amount, x, y) {
		self.setScore(self.score + amount);
		// Update the registry
		UPGRADE_REGISTRY.gold = self.score;
		SaveManager.save();
		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);
		}
	};
	// Create UI elements
	self.scoreText = new CustomText('0', {
		size: 100,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	// Initialize with default position
	self.scoreText.x = -100;
	// AFTER UI elements exist AND methods are defined, load saved score
	if (storage.gold) {
		self.score = Number(storage.gold);
		self.setScore(self.score); // Now this is safe because scoreText exists and setScore is defined
	}
	self.coinIcon = LK.getAsset('coin', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 2,
		scaleY: 2
	});
	// New ammo display
	self.ammoText = new CustomText('0*', {
		size: 90,
		fill: 0xFFFFFF,
		letterSpacing: 5
	});
	self.ammoIcon = LK.getAsset('arrow', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.3,
		scaleY: 1.3,
		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
	// Position elements
	self.scoreText.x = -200; // Less negative value
	self.scoreText.y = 0; // Center vertically
	self.coinIcon.x = 0;
	self.coinIcon.y = 0;
	self.ammoText.x = 30;
	self.ammoText.y = 170; // Place below coin display
	self.ammoIcon.x = 55;
	self.ammoIcon.y = 240; // Place below coin display
	// Add ammo update method
	self.updateAmmo = function (amount) {
		self.ammoText.setText(amount + '*');
		// Instead of hardcoded adjustments based on digit count,
		// calculate the width of the text and properly align it
		// Keep the right edge at a fixed position
		var rightEdgePosition = 30; // Fixed right edge position
		self.ammoText.x = rightEdgePosition - self.ammoText.width;
	};
	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 RICOCHET_SEARCH_RADIUS_SQ = 500 * 500; // Moved to Constants section
// var RICOCHET_ARROW_SPEED = 40; // Moved to Constants section
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 arrowParticles = []; // Array to hold arrow trail particles
var jarSpawnCounter = 0;
var jarSpawnInterval = 10;
var chestSpawnCounter = 0;
var chestSpawnInterval = 75;
var enemies = [];
var enemySpawnCounter = 0;
var enemySpawnInterval = 100;
var eyeballSpawnCounter = 0;
var eyeballSpawnInterval = 200;
var particleSystem;
var heartContainer = new HeartContainer();
scoreManager = new ScoreManager(); // Create it early
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,
	tint: 0x777777
}));
var mg2 = midgroundContainer.addChild(LK.getAsset('midground', {
	anchorX: 0,
	anchorY: 1,
	tint: 0x777777
}));
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,
	tint: 0x777777
}));
var fg2 = foregroundContainer.addChild(LK.getAsset('foreground', {
	anchorX: 0,
	anchorY: 1,
	tint: 0x777777
}));
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);
}
function createGameplayShopButton() {
	// Create shop button in top corner
	var shopButton = game.addChild(LK.getAsset('shopbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.7,
		scaleY: 0.7
	}));
	shopButton.x = 250;
	shopButton.y = 310; // Position below score display
	shopButton.down = function () {
		// Pause the game
		var previousGameSpeed = gameSpeedMultiplier;
		// IMPORTANT FIX: Force cancel any active movement states
		if (player) {
			// Cancel slide if active
			if (player.isSliding) {
				player.isSliding = false;
				player.slideTimer = 0;
				player.hitboxHeight = player.normalHitboxHeight;
			}
			// Cancel air dash if active
			if (player.isAirDashing) {
				player.isAirDashing = false;
				player.airDashTimer = 0;
				// Clean up shadows
				player.shadowImages.forEach(function (shadow) {
					if (shadow.parent) {
						shadow.destroy();
					}
				});
				player.shadowImages = [];
				// Reset rotation
				player.rotation = 0;
			}
		}
		// Now set game speed to 0
		gameSpeedMultiplier = 0;
		// Open shop
		openGameplayShop();
		// Function to return to game
		function resumeGame() {
			// Close shop
			if (game.shopManager && game.shopManager.parent) {
				game.shopManager.destroy();
				game.shopManager = null;
			}
			// Restore game speed, but ensure it's not the enhanced speed from slide/dash
			gameSpeedMultiplier = 1.0; // IMPORTANT FIX: Always restore to 1.0, not previousGameSpeed
			// Remove back button using our reference
			if (gameplayBackButton && gameplayBackButton.parent) {
				gameplayBackButton.destroy();
			}
		}
		// Add custom back button for gameplay shop
		gameplayBackButton = game.addChild(LK.getAsset('backbutton', {
			anchorX: 0.5,
			anchorY: 0.5,
			scaleX: 0.75,
			scaleY: 0.75,
			x: GAME_WIDTH / 2,
			y: GAME_HEIGHT - 200
		}));
		gameplayBackButton.down = function () {
			LK.getSound('menuselect').play();
			resumeGame();
		};
	};
	return shopButton;
}
// Modified openShop function for gameplay
function openGameplayShop() {
	SaveManager.load();
	// Create shop if it doesn't exist
	if (!game.shopManager) {
		game.shopManager = new ShopManager();
		game.shopManager.x = GAME_WIDTH / 2;
		game.shopManager.y = GAME_HEIGHT / 2;
	}
	// Show shop and force an update
	game.addChild(game.shopManager);
	game.shopManager.updateTotalGold();
	// Override the purchase function to apply upgrades immediately
	var originalPurchase = game.shopManager.purchaseSelectedUpgrade;
	game.shopManager.purchaseSelectedUpgrade = function () {
		originalPurchase.call(game.shopManager);
		SaveManager.applyUpgradeEffects(player);
	};
}
function applyPlayerUpgradeEffects() {
	if (!player) {
		return;
	}
	// Apply health upgrade if saved
	if (storage.playerMaxHealth) {
		player.maxHealth = parseInt(storage.playerMaxHealth);
		player.currentHealth = player.maxHealth;
		player.heartContainer.updateHealth(player.currentHealth);
	}
	// Apply arrow capacity upgrade if saved
	if (storage.playerArrowCapacity) {
		player.ammoCount = parseInt(storage.playerArrowCapacity);
		scoreManager.updateAmmo(player.ammoCount);
	}
	// Apply levitate upgrade if saved
	if (storage.playerHasLevitate) {
		player.hasLevitate = true;
	}
	// Apply any other player effect upgrades here
}
// 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 / 3;
	tutorialButton.y = GAME_HEIGHT / 1.15;
	tween(tutorialButton, {
		alpha: 1
	}, {
		duration: 1000,
		easing: tween.easeIn
	});
	var shopButton = titleScreen.attachAsset('shopbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0
	});
	shopButton.x = GAME_WIDTH / 1.4;
	shopButton.y = GAME_HEIGHT / 1.15; // Position between play and tutorial buttons
	shopButton.down = function () {
		// Add visual feedback animation
		tween(shopButton, {
			scaleX: 0.9,
			scaleY: 0.9
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(shopButton, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						openShop();
					}
				});
			}
		});
		// Play menu select sound
		LK.getSound('menuselect').play();
	};
	tween(shopButton, {
		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();
}
function resetAllStorage() {
	// Log the current state
	// Delete all stored values
	storage.gold = 0;
	storage.healthUpgrade = 0;
	storage.arrowUpgrade = 0;
	storage.levitateUpgrade = 0;
	storage.slideUpgrade = 0;
	storage.lootUpgrade = 0;
	storage.groundSlamUpgrade = 0;
	storage.potionChanceUpgrade = 0;
	storage.arrowFinderUpgrade = 0;
}
function openShop() {
	// Hide title screen
	titleScreen.visible = false;
	SaveManager.load();
	// Create shop if it doesn't exist
	if (!game.shopManager) {
		game.shopManager = new ShopManager();
		game.shopManager.x = GAME_WIDTH / 2;
		game.shopManager.y = GAME_HEIGHT / 2;
	}
	// Show shop and force an update
	game.addChild(game.shopManager);
	game.shopManager.updateTotalGold();
	// Add back button
	var backButton = game.shopManager.attachAsset('backbutton', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.75,
		scaleY: 0.75,
		x: 0,
		y: 1100
	});
	backButton.down = function () {
		// Add visual feedback animation
		tween(backButton, {
			scaleX: 0.65,
			scaleY: 0.65
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(backButton, {
					scaleX: 0.75,
					scaleY: 0.75
				}, {
					duration: 200,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						closeShop();
					}
				});
			}
		});
		// Play menu select sound
		LK.getSound('menuselect').play();
	};
}
function closeShop() {
	// Save the current state before closing
	SaveManager.save();
	if (game.shopManager && game.shopManager.parent) {
		game.shopManager.destroy(); // Destroy the manager and all its children
		game.shopManager = null; // Clear the reference
	}
	// Ensure title screen is visible after shop closes
	if (titleScreen && !titleScreen.parent) {
		// If title screen was destroyed or removed, recreate it
		createTitleScreen();
	} else if (titleScreen) {
		titleScreen.visible = true;
	}
}
// 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
	SaveManager.load();
	player = game.addChild(new Player());
	player.x = GAME_WIDTH / 4.5;
	player.y = GAME_HEIGHT / 1.5 - 100;
	game.addChild(heartContainer);
	SaveManager.applyUpgradeEffects(player);
	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
	game.addChild(scoreManager.container);
	initializeGame();
	// Initialize particle system
	particleSystem = new ParticlePool(100);
	game.addChild(particleSystem);
	// Add shop button to gameplay
	createGameplayShopButton();
	// Show health
	player.heartContainer.alpha = 1;
	player.heartVisibilityTimer = 120;
	// Play background music
	LK.playMusic('backgroundmusic1', {
		fade: {
			start: 0,
			end: 0.7,
			duration: 3500
		}
	});
}
// 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;
		// Calculate trap platform chance based on time
		var baseTrapChance = 0.01; // 1% base chance
		var additionalChance = Math.min(0.15, timeSinceStart / 300000); // Up to 15% extra after 5 minutes
		var totalTrapChance = baseTrapChance + additionalChance;
		if (currentPlatformPattern === 'C') {
			// Single platform for straight sections, using stored height
			var platform;
			if (Math.random() < totalTrapChance) {
				platform = new CrumblePlatform();
			} else {
				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, platformLower;
			// Create upper platform
			if (Math.random() < totalTrapChance) {
				platformUpper = new CrumblePlatform();
			} else {
				platformUpper = new Platform();
			}
			// Create lower platform
			if (Math.random() < totalTrapChance) {
				platformLower = new CrumblePlatform();
			} else {
				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 && !(p instanceof CrumblePlatform);
		});
		// 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 && !(p instanceof CrumblePlatform);
		});
		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 (game.shopManager && game.shopManager.parent) {
		return;
	}
	if (!tutorialActive) {
		var isPlatformOccupied = function isPlatformOccupied(platform) {
			return enemies.some(function (enemy) {
				return enemy.currentPlatform === platform && (enemy.type === 'goblin' || enemy.type === 'skeleton' || enemy.type === 'mushroom');
			});
		};
		// Get available platforms for ground enemies
		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);
		});
		// Unified enemy spawn counter for all ground enemies
		enemySpawnCounter++;
		// If we have available platforms and it's time to spawn
		if (enemySpawnCounter >= enemySpawnInterval && unoccupiedPlatforms.length > 0) {
			// Decide how many enemies to spawn (1-3 based on available platforms)
			var spawnCount = Math.min(Math.floor(Math.random() * 3) + 1, unoccupiedPlatforms.length);
			for (var i = 0; i < spawnCount; i++) {
				// Choose a random platform from unoccupied ones
				var platformIndex = Math.floor(Math.random() * unoccupiedPlatforms.length);
				var platform = unoccupiedPlatforms[platformIndex];
				// Choose enemy type randomly with weighted probabilities
				var enemyRandom = Math.random();
				var enemyType;
				if (enemyRandom < 0.45) {
					enemyType = 'goblin'; // 45% chance for goblin
				} else if (enemyRandom < 0.75) {
					enemyType = 'mushroom'; // 30% chance for mushroom
				} else {
					enemyType = 'skeleton'; // 25% chance for skeleton
				}
				// Create the enemy
				var enemy = new Enemy(enemyType);
				enemy.x = platform.x;
				enemy.y = platform.y - ENEMY_PLATFORM_OFFSET;
				enemy.currentPlatform = platform;
				enemies.push(enemy);
				game.addChild(enemy);
				// Remove the used platform from available platforms
				unoccupiedPlatforms.splice(platformIndex, 1);
			}
			// Reset the counter with a random interval
			enemySpawnInterval = Math.floor(Math.random() * 100) + 150;
			enemySpawnCounter = 0;
		}
		// Eyeball spawning - kept separate since they don't use platforms
		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;
			enemies.push(enemy);
			game.addChild(enemy);
			// Random interval between 200-400 ticks
			eyeballSpawnInterval = Math.floor(Math.random() * 200) + 200;
			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();
					}
					// Ricochet logic for tracking arrows
					if (UPGRADE_REGISTRY.tracking_arrow > 0) {
						var firstHitEnemy = enemies[j]; // The enemy that was just hit by 'arrow'
						var closestSecondEnemy = null;
						var minDistanceSqToSecondEnemy = RICOCHET_SEARCH_RADIUS_SQ;
						for (var k = 0; k < enemies.length; k++) {
							var potentialSecondEnemy = enemies[k];
							// Ensure we're not targeting the same enemy, and the potential target is valid and in front of the player
							if (potentialSecondEnemy === firstHitEnemy || !potentialSecondEnemy || potentialSecondEnemy.isDying || potentialSecondEnemy.isHit || potentialSecondEnemy.destroyed || player && potentialSecondEnemy.x < player.x) {
								// Check against player's X position
								continue;
							}
							var dxRicochet = potentialSecondEnemy.x - firstHitEnemy.x;
							var dyRicochet = potentialSecondEnemy.y - firstHitEnemy.y;
							var distSqRicochet = dxRicochet * dxRicochet + dyRicochet * dyRicochet;
							if (distSqRicochet < minDistanceSqToSecondEnemy) {
								minDistanceSqToSecondEnemy = distSqRicochet;
								closestSecondEnemy = potentialSecondEnemy;
							}
						}
						if (closestSecondEnemy) {
							var ricochetArrow = new Arrow();
							ricochetArrow.x = firstHitEnemy.x;
							ricochetArrow.y = firstHitEnemy.y;
							// Set startX/Y for tracking logic, relative to the new arrow's origin
							ricochetArrow.startX = ricochetArrow.x;
							ricochetArrow.startY = ricochetArrow.y;
							// Calculate the angle to the target and set it as the arrow's rotation
							ricochetArrow.rotation = Math.atan2(closestSecondEnemy.y - ricochetArrow.y, closestSecondEnemy.x - ricochetArrow.x);
							// Set the sprite rotation to match immediately
							ricochetArrow.sprite.rotation = ricochetArrow.rotation;
							// IMPORTANT: Update the velocity components to match the rotation
							ricochetArrow.velocityX = Math.cos(ricochetArrow.rotation) * RICOCHET_ARROW_SPEED;
							ricochetArrow.velocityY = Math.sin(ricochetArrow.rotation) * RICOCHET_ARROW_SPEED;
							ricochetArrow.speed = RICOCHET_ARROW_SPEED;
							ricochetArrow.damage = arrow.damage;
							game.addChild(ricochetArrow);
							arrows.push(ricochetArrow);
						}
					}
					// 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);
			}
		}
		// In the updateEnemies function where spore collisions are checked
		if (window.spores) {
			for (var i = window.spores.length - 1; i >= 0; i--) {
				var spore = window.spores[i];
				// Skip if spore is invalid or already marked for removal
				if (!spore || !spore.parent || spore.destroyed) {
					window.spores.splice(i, 1);
					continue;
				}
				// Update spore
				spore.update();
				// Only check collisions if the spore is active and player can take damage
				if (spore.isActive && !player.isInvulnerable && !player.isDying) {
					if (GameUtils.checkCollision(spore.getBounds(), player.getCollisionBounds())) {
						player.takeDamage();
						spore.hasHitPlayer = true;
						spore.explode();
					}
				}
				// Safety check - if spore is destroyed but still in array, remove it
				if (spore.destroyed) {
					window.spores.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 ((slideAttackBounds || player.isAirDashing && playerBounds) && !enemies[j].isHit && !enemies[j].isDying) {
			if (enemies[j].x > player.x && GameUtils.checkCollision(player.isAirDashing ? playerBounds : slideAttackBounds, enemyBounds)) {
				enemies[j].slideHit();
				continue;
			}
		}
		if (GameUtils.checkCollision(playerBounds, enemyBounds)) {
			if (!enemies[j].isHit && !enemies[j].isDying) {
				player.takeDamage();
			}
		}
	}
}
if (!game.saveManager) {
	game.saveManager = SaveManager;
	SaveManager.load();
}
// 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) {
		// Add shop update here if shop is open
		if (game.shopManager) {
			game.shopManager.update();
		}
		return;
	}
	// Update player and particles
	player.update();
	if (particleSystem) {
		particleSystem.update();
	}
	// Update arrows
	for (var i = arrows.length - 1; i >= 0; i--) {
		var arrow_to_check = arrows[i]; // Use a temporary variable for clarity
		arrow_to_check.update();
		// Check if arrow is outside screen boundaries and handle destruction
		if (arrow_to_check.x > GAME_WIDTH || arrow_to_check.x < 0 || arrow_to_check.y > GAME_HEIGHT || arrow_to_check.y < 0) {
			arrow_to_check.destroy();
			arrows.splice(i, 1);
			continue; // Arrow removed, skip to next iteration
		}
		// Fallback check: if arrow was destroyed by other means (e.g., enemy collision)
		// Note: Enemy collision logic in updateEnemies also splices. This is a safeguard.
		if (arrow_to_check.destroyed) {
			arrows.splice(i, 1);
			// No continue needed here as it's the last check for this arrow in this part of the loop
		}
	}
	for (var i = game.children.length - 1; i >= 0; i--) {
		var child = game.children[i];
		if (child instanceof CrumblePiece) {
			child.update();
		}
	}
	// Update arrow trail particles
	for (var k = arrowParticles.length - 1; k >= 0; k--) {
		var p = arrowParticles[k];
		p.update();
		// Check if particle is marked for destruction by its tween and is visually faded
		if (p.destroyQueued && p.sprite.alpha < 0.05) {
			p.destroy();
			arrowParticles.splice(k, 1);
		} else if (p.x < -50 || p.x > GAME_WIDTH + 50 || p.y < -50 || p.y > GAME_HEIGHT + 50) {
			// Fallback: destroy if way off-screen (e.g. if tween was interrupted or didn't complete)
			p.destroy();
			arrowParticles.splice(k, 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 (game.shopManager && game.shopManager.parent) {
		return;
	}
	if (!gameStarted) {
		// Don't process button clicks if shop is open
		if (game.shopManager && game.shopManager.parent) {
			return;
		}
		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) {
			// Check if the button has already been pressed
			if (game.playButtonPressed) {
				return;
			}
			game.playButtonPressed = true;
			// Stop the flashing animation
			if (playButtonFlashInterval) {
				LK.clearInterval(playButtonFlashInterval);
				playButtonFlashInterval = null;
				// Ensure button is fully visible before animation
				playButton.alpha = 1;
			}
			// Play start game sound effect
			LK.getSound('startgame').play();
			// Add scale animation
			tween(playButton, {
				scaleX: 0.8,
				scaleY: 0.8
			}, {
				duration: 150,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(playButton, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 250,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							// Start game after a 1 second delay
							LK.setTimeout(function () {
								startGame();
							}, 900);
						}
					});
				}
			});
		} else if (x >= tutorialButtonBounds.left && x <= tutorialButtonBounds.right && y >= tutorialButtonBounds.top && y <= tutorialButtonBounds.bottom) {
			// Add visual feedback animation
			tween(tutorialButton, {
				scaleX: 0.9,
				scaleY: 0.9
			}, {
				duration: 100,
				easing: tween.easeOut,
				onFinish: function onFinish() {
					tween(tutorialButton, {
						scaleX: 1,
						scaleY: 1
					}, {
						duration: 200,
						easing: tween.easeOut,
						onFinish: function onFinish() {
							startTutorial();
						}
					});
				}
			});
			// Play menu select sound
			LK.getSound('menuselect').play();
			return;
		}
		return;
	}
	// Prevent input if player is dying or air dying
	if (player && (player.isDying || player.dyingInAir)) {
		return;
	}
	if (tutorialActive && tutorialManager) {
		tutorialManager.inputProcessed = false; // Reset the flag on touch end
	}
	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 > 50) {
			if (player.isOnGround) {
				player.slide();
			} else {
				player.airDash();
			}
		} else if (deltaX < -50) {
			// Left swipe - trigger bow shot
			player.shoot();
		}
	} else if (deltaY > 120) {
		// Downward swipe
		if (!player.isOnGround) {
			player.groundSlam(); // Call ground slam FIRST when in air
		} else {
			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;
	lastMoveY = y;
};
: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/67f7453b03de48563abd51dc.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7f7d1dbbedb25886c4a1a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/67f7f831dbbedb25886c4a31.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/67f9ba2601e5b663933736ab.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/6809633bc8e430ed8ad2c650.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