/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	playerDeaths: 0,
	monsterDeaths: 0
});
/**** 
* Classes
****/ 
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	var bulletGraphics = self.attachAsset('enemy2_horn', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 10;
	self.update = function () {
		self.x -= self.speed;
		if (self.speed < 0) {
			self.rotation += 0.1; // Add spinning effect
			// Check collision with enemies
			for (var i = 0; i < enemyContainer.children.length; i++) {
				var enemy = enemyContainer.children[i];
				if (self.intersects(enemy.bodyCollider)) {
					enemy.sendEvent(GameEvents.BULLET_HIT, self);
				}
			}
		}
		if (self.x < 0) {
			self.destroy();
		} else if (self.x > 2048 + 200) {
			self.destroy();
		}
	};
});
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy1_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('enemy1_walk1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('enemy1_walk2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horns": LK.getAsset('enemy1_horns', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	//replacing the asset ref with the image added.
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	//configure the animation frames.
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'dead': new FrameAnimation([self.images.dead], 100)
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	// Add a red rectangle to the enemy's head
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 100
	});
	self.headCollider.y = -enemyGraphics.height / 4; // Position the rectangle on the enemy's head
	self.headCollider.alpha = 0;
	// Add a bodyCollider rectangle to the bottom part of the enemy
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00
	});
	self.bodyCollider.y = enemyGraphics.height / 4; // Position the rectangle on the enemy's body
	self.bodyCollider.alpha = 0;
	self.speed = 5;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
			},
			onExit: function onExit() {
				console.log('Enemy stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (self.hasHorns() == false) {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				tween(monsterDeathsText.scale, {
					x: 1.5,
					y: 1.5
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(monsterDeathsText.scale, {
							x: 1,
							y: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6 // 30 degrees in radians
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	// Function to check if the enemy has visible horns
	self.hasHorns = function () {
		return self.images['horns'] && self.images['horns'].visible;
	};
});
var Enemy2 = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy2_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('enemy2_walk1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('enemy2_walk2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"attack1": LK.getAsset('enemy2_attack1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"attack2": LK.getAsset('enemy2_attack2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horn": LK.getAsset('enemy2_horn', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle1": LK.getAsset('enemy2_idle1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle2": LK.getAsset('enemy2_idle2', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'dead': new FrameAnimation([self.images.dead], 100),
		'idle': new FrameAnimation([self.images.idle1, self.images.idle2], 300),
		// New idle animation
		'attack': new FrameAnimation([self.images.attack1, self.images.attack2], 300) // New attack animation
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 100
	});
	self.headCollider.y = -enemyGraphics.height / 4;
	self.headCollider.alpha = 0;
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00
	});
	self.bodyCollider.y = enemyGraphics.height / 4;
	self.bodyCollider.alpha = 0;
	self.speed = 5;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy2 starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
				// Transition to attack state when reaching 2/3rd of the screen
				if (self.lastX > 2048 * (2 / 3) && self.x <= 2048 * (2 / 3)) {
					self.setState('attack');
				}
				self.lastX = self.x;
			},
			onExit: function onExit() {
				console.log('Enemy2 stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					self.setState('dead');
				}
			}
		},
		attack: {
			// New attack state
			onEnter: function onEnter() {
				console.log('Enemy2 starts attacking');
				self.anims['idle'].play(); // Play idle animation
				self.attackTimer = 500; // Set attack timer
			},
			onUpdate: function onUpdate() {
				if (self.attackTimer > 0) {
					self.attackTimer--; // Decrease attack timer
				} else {
					self.anims['idle'].stop(); // Stop idle animation
					self.anims['attack'].play(); // Play attack animation
					if (isSoundOn) {
						LK.getSound('roar').play();
					}
					self.attackTimer = 1000;
				}
			},
			onExit: function onExit() {
				console.log('Enemy2 stops attacking');
				self.anims['idle'].stop();
				self.anims['attack'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in attack state: ', event, data);
				if (event === GameEvents.ANIM_FINISHED && data == self.anims['attack']) {
					// Shoot a bullet at the player using enemy_horn image
					var bullet = new Bullet(self.images.horn);
					bullet.x = self.x;
					bullet.y = self.y;
					bulletContainer.addChild(bullet);
					self.anims['attack'].stop();
					self.anims['idle'].play();
					self.attackTimer = 500;
				} else if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					self.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		// End of new attack state
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy2 has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	// Function to check if the enemy has visible horns
	self.hasHorns = function () {
		return self.images['horn'] && self.images['horn'].visible;
	};
});
var Enemy4 = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy4_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle1": LK.getAsset('enemy4_idle1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle2": LK.getAsset('enemy4_idle2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horns": LK.getAsset('enemy1_horns', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	self.anims = {
		'walk': new FrameAnimation([self.images.idle1, self.images.idle2], 300),
		'dead': new FrameAnimation([self.images.dead], 100)
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 120,
		height: 100,
		x: -200,
		y: 150 - 50
	});
	self.headCollider.alpha = 0;
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 200,
		x: 0
	});
	self.bodyCollider.y = enemyGraphics.height / 4;
	self.bodyCollider.alpha = 0;
	self.speed = 8;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy4 starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
				if (self.x < -500) {
					self.setState('dead');
				}
			},
			onExit: function onExit() {
				console.log('Enemy4 stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					data.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					// Do nothing for Enemy4 when hit by a bullet
				} else if (event == GameEvents.COLLISION_PLAYER_BODY) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy4 has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	self.hasHorns = function () {
		return true;
	};
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"idle": LK.getAsset('player', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"jump": LK.getAsset('player_jump', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('walk_1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('walk_2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'dead': LK.getAsset('player_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'attack1': LK.getAsset('player_attack1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'attack2': LK.getAsset('player_attack2', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	self.speed = 5;
	self.jumpHeight = 40;
	self.isJumping = false;
	self.velocityY = 0;
	self.state;
	//replacing the asset ref with the image added.
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	// Add a bodyCollider rectangle to the player character
	self.bodyCollider = self.attachAsset('collider', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00,
		// Yellow color
		alpha: 0
	});
	self.bodyCollider.y = self.images.idle.height / 4; // Position the rectangle on the player's body
	//configure the animation frames.
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'jump': new FrameAnimation([self.images.jump], 100),
		'idle': [self.images.idle],
		'dead': new FrameAnimation([self.images.dead], 100),
		'attack': new FrameAnimation([self.images.jump, self.images.attack1, self.images.attack2, self.images.idle], 200)
	};
	self.states = {
		idle: {
			onEnter: function onEnter() {
				console.log('Entering idle state');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting idle state');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (data instanceof Enemy4) {
						//do nothing.
					} else {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Player has died');
				if (isSoundOn) {
					LK.getSound('player_hit').play();
				}
				playerDeaths++;
				storage.playerDeaths = playerDeaths;
				playerDeathsText.setText('' + playerDeaths);
				tween(playerDeathsText.scale, {
					x: 1.5,
					y: 1.5
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(playerDeathsText.scale, {
							x: 1,
							y: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
				tween(self, {
					y: self.y - 400,
					rotation: -Math.PI / 6 // 30 degrees in radians
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 1000,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								LK.showGameOver();
							}
						});
					}
				});
				// Stop all enemies from moving
				for (var j = 0; j < enemies.length; j++) {
					enemies[j].speed = 0;
				}
				self.anims['dead'].play();
				// Additional logic for death state can be added here
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting death state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in death state: ', event, data);
			}
		},
		attack: {
			onEnter: function onEnter() {
				console.log('Player is attacking');
				if (isSoundOn) {
					LK.getSound('punch_swing').play();
				}
				self.anims['attack'].play();
			},
			onUpdate: function onUpdate() {
				// Attack logic can be added here
			},
			onExit: function onExit() {
				console.log('Exiting attack state');
				self.anims['attack'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in attack state: ', event, data);
				if (event === GameEvents.ANIM_FINISHED) {
					self.setState('idle');
				} else if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (data.hasHorns()) {
						self.setState('dead');
					} else {
						data.setState('dead');
					}
				} else if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (isSoundOn) {
						LK.getSound('punch').play();
					}
					data.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					if (data.speed > 0) {
						data.speed = -data.speed;
						if (isSoundOn) {
							LK.getSound('punch').play();
						}
					} // Reverse bullet direction
				}
			}
		},
		jumping: {
			onEnter: function onEnter() {
				if (!self.isJumping) {
					if (isSoundOn) {
						LK.getSound('jump').play();
					}
					self.isJumping = true;
					self.velocityY = -self.jumpHeight;
					self.anims['jump'].play();
					console.log("jump Started");
				}
			},
			onUpdate: function onUpdate() {
				if (self.isJumping) {
					self.y += self.velocityY;
					self.velocityY += 1.4; // Increase gravity effect to make descent twice as fast 
					if (self.y >= 2732 / 2) {
						// Ground level
						self.y = 2732 / 2;
						self.isJumping = false;
						self.velocityY = 0;
						self.setState('idle');
					}
				}
			},
			onExit: function onExit() {
				console.log('Exiting jumping state');
				self.anims['jump'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in jumping state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (data.hasHorns()) {
						self.setState('dead');
					} else {
						// Add upward velocity to player
						player.velocityY = -player.jumpHeight / 2;
						player.isJumping = true;
						if (isSoundOn) {
							LK.getSound('bounce').play();
						}
					}
				} else if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (data instanceof Enemy4) {
						// Add upward velocity to player
						player.velocityY = -player.jumpHeight / 2;
						player.isJumping = true;
						if (isSoundOn) {
							LK.getSound('bounce').play();
						}
					} else {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.attack = function () {
		if (self.state !== 'jumping' && self.state !== 'dead') {
			self.setState('attack');
		}
	};
	self.jump = function () {
		if (self.state !== 'jumping' && self.state !== 'dead') {
			self.setState('jumping');
		}
	};
	//call the initial state.
	self.setState('idle');
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87CEEB // Sky blue background
});
/**** 
* Game Code
****/ 
function onEnemyDeath(enemy) {
	LK.setScore(LK.getScore() + 1);
	scoreText.setText(LK.getScore());
	// Increment the count of enemies killed by the player
	enemiesKilledByPlayer++;
	monsterDeathsText.setText('' + monsterDeaths);
	// Check if the score is 8 or above and 5 enemies have been killed
	if (LK.getScore() >= 8 && enemiesKilledByPlayer % 5 === 0) {
		// Spawn Enemy4
		var enemy4 = new Enemy4();
		enemy4.x = 2048;
		enemy4.y = 2732 / 2;
		enemies.push(enemy4);
		enemyContainer.addChild(enemy4);
	}
}
if (typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true) {
	LK.playMusic('platformer_music'); // Start playing background music
}
var GameEvents = {
	COLLISION_ENEMY_HEAD: 0,
	COLLISION_ENEMY_BODY: 1,
	COLLISION_PLAYER_BODY: 2,
	ANIM_FINISHED: 3,
	BULLET_HIT: 4 // New event for when player is hit by enemy bullet
};
// Initialize death counters from storage
var playerDeaths = storage.playerDeaths;
var monsterDeaths = storage.monsterDeaths;
// Track the number of enemies killed by the player
var enemiesKilledByPlayer = 0;
// Define a class for frame animations
var FrameAnimation = function FrameAnimation(assets, frameRate) {
	var self = this;
	self.assets = assets;
	self.frameRate = frameRate;
	self.currentFrame = 0;
	self.isPlaying = false;
	self.interval = null;
	// Function to show the current frame
	self.showFrame = function () {
		for (var i = 0; i < self.assets.length; i++) {
			self.assets[i].visible = false;
		}
		self.assets[self.currentFrame].visible = true;
	};
	// Play the animation
	self.play = function () {
		if (!self.isPlaying) {
			self.isPlaying = true;
			self.showFrame();
			if (self.assets.length > 1) {
				self.interval = LK.setInterval(function () {
					self.currentFrame = (self.currentFrame + 1) % self.assets.length;
					self.showFrame();
					if (self.currentFrame === 0) {
						// Send ANIM_FINISHED event when animation completes a cycle
						self.assets[0].parent.sendEvent(GameEvents.ANIM_FINISHED, self);
					}
				}, self.frameRate);
			}
		}
	};
	// Pause the animation
	self.pause = function () {
		if (self.isPlaying) {
			clearInterval(self.interval);
			self.isPlaying = false;
		}
	};
	// Resume the animation
	self.resume = function () {
		if (!self.isPlaying) {
			self.play();
		}
	};
	// Stop the animation
	self.stop = function () {
		LK.clearInterval(self.interval);
		self.isPlaying = false;
		self.currentFrame = 0;
		for (var i = 0; i < self.assets.length; i++) {
			self.assets[i].visible = false;
		}
	};
};
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5
}));
background.x = 2048 / 2;
background.y = 2732 / 2 - 550;
// Initialize player
var player = game.addChild(new Player());
player.x = 150; // Position player on the left side of the screen
player.y = 2732 / 2;
// Initialize enemy container
var enemyContainer = new Container();
game.addChild(enemyContainer);
// Initialize enemies array
var enemies = [];
// Initialize bullet container
var bulletContainer = new Container();
game.addChild(bulletContainer);
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score
var scoreText = new Text2('0', {
	size: 100,
	fill: 0xFFFFFF
});
// Create a GUI container
var guiContainer = new Container();
game.addChild(guiContainer);
// Add the score text to the GUI container at the top center of the screen
guiContainer.addChild(scoreText);
scoreText.x = 2048 / 2;
scoreText.y = 20; // Adjusted to align with other top elements
scoreText.anchor.set(0.5, 0); // Center the score text horizontally
// Add a jump button to the bottom of the screen in the GUI container
var jumpButton = LK.getAsset('jump_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 400,
	height: 400
});
jumpButton.x = 2048 / 4;
jumpButton.y = 2732 - jumpButton.height / 2 - 300;
guiContainer.addChild(jumpButton);
// Add an attack button to the bottom of the screen in the GUI container
var attackButton = LK.getAsset('attack_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 400,
	height: 400
});
attackButton.x = 3 * 2048 / 4;
attackButton.y = 2732 - attackButton.height / 2 - 300;
guiContainer.addChild(attackButton);
// Add event listeners to flash buttons black on mouse or touch down
jumpButton.down = function (x, y, obj) {
	LK.effects.flashObject(jumpButton, 0x000000, 500);
	player.jump();
};
attackButton.down = function (x, y, obj) {
	LK.effects.flashObject(attackButton, 0x000000, 500);
	player.attack();
};
// Add a settings button to the top right of the screen
var settingsButton = LK.getAsset('settings_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
settingsButton.x = 2048 - settingsButton.width / 2 - 50;
settingsButton.y = settingsButton.height / 2 + 50;
guiContainer.addChild(settingsButton);
// Add a music button below the settings button, initially hidden
var musicButton = LK.getAsset('music_on', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
musicButton.x = settingsButton.x;
musicButton.y = settingsButton.y + settingsButton.height / 2 + musicButton.height / 2 + 30;
guiContainer.addChild(musicButton);
music_off = LK.getAsset('music_off', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200,
	x: musicButton.x,
	y: musicButton.y,
	visible: false
});
guiContainer.addChild(music_off);
// Add a sound button below the music button, initially hidden
var soundButton = LK.getAsset('sound_on', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
soundButton.x = musicButton.x;
soundButton.y = musicButton.y + musicButton.height + 30;
guiContainer.addChild(soundButton);
var sound_off = LK.getAsset('sound_off', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200,
	x: soundButton.x,
	y: soundButton.y,
	visible: false
});
guiContainer.addChild(sound_off);
// Initially hide music and sound buttons
musicButton.visible = false;
music_off.visible = false;
soundButton.visible = false;
sound_off.visible = false;
// Toggle music and sound button visibility when settings button is pressed
settingsButton.down = function (x, y, obj) {
	var newVisible = !musicButton.visible;
	musicButton.visible = newVisible;
	music_off.visible = newVisible && !isMusicPlaying;
	soundButton.visible = newVisible;
	sound_off.visible = newVisible && !isSoundOn;
};
// Add event listener to toggle music on button press
var isMusicPlaying = typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true; // Initialize music state from storage
// Set initial visibility for music and sound buttons based on storage and settings toggle
var isMusicPlaying = typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true; // Initialize music state from storage
var isSoundOn = typeof storage.isSoundOn === "boolean" ? storage.isSoundOn : true; // Initialize sound state from storage
// When music button is pressed, toggle music and update icons
musicButton.down = function (x, y, obj) {
	LK.effects.flashObject(musicButton, 0x000000, 500);
	LK.effects.flashObject(music_off, 0x000000, 500);
	if (isMusicPlaying) {
		LK.stopMusic(); // Stop playing background music
		music_off.visible = true;
		musicButton.visible = true;
	} else {
		LK.playMusic('platformer_music'); // Start playing background music
		music_off.visible = false;
		musicButton.visible = true;
	}
	isMusicPlaying = !isMusicPlaying; // Toggle music state
	storage.isMusicPlaying = isMusicPlaying; // Persist to storage
};
// When sound button is pressed, toggle sound and update icons
soundButton.down = function (x, y, obj) {
	LK.effects.flashObject(soundButton, 0x000000, 500);
	LK.effects.flashObject(sound_off, 0x000000, 500);
	if (isSoundOn) {
		sound_off.visible = true;
		soundButton.visible = true;
	} else {
		sound_off.visible = false;
		soundButton.visible = true;
	}
	isSoundOn = !isSoundOn;
	storage.isSoundOn = isSoundOn; // Persist to storage
};
// Add the score_display image to the GUI container at the top center of the screen
var scoreDisplayImage = LK.getAsset('score_display', {
	anchorX: 0.5,
	anchorY: 0.0
});
scoreDisplayImage.x = 2048 / 2;
scoreDisplayImage.y = 0; // Align to the top of the screen
guiContainer.addChild(scoreDisplayImage);
// Create Text2 objects to display player and monster deaths
var playerDeathsText = new Text2('' + playerDeaths, {
	size: 72,
	fill: 0xFFFFFF,
	anchorX: 0.5,
	anchorY: 0.5,
	font: "VAG Rounded" // More rounded and thick font 
});
var monsterDeathsText = new Text2('' + monsterDeaths, {
	size: 72,
	fill: 0xFFFFFF,
	anchorX: 0.5,
	anchorY: 0.5,
	font: "VAG Rounded" // More rounded and thick font 
});
// Position the death counters at the top of the screen
playerDeathsText.x = scoreDisplayImage.x + 150;
playerDeathsText.y = 190;
monsterDeathsText.x = scoreDisplayImage.x - 280;
monsterDeathsText.y = 190;
// Add the death counters to the GUI container
guiContainer.addChild(playerDeathsText);
guiContainer.addChild(monsterDeathsText);
function addEnemy2(game, enemies) {
	enemy = new Enemy2();
	enemy.x = 2048;
	enemy.y = 2732 / 2;
	enemies.push(enemy);
	enemyContainer.addChild(enemy);
}
// Handle game updates
game.update = function () {
	player.update();
	// Spawn enemies
	if (player.state !== 'dead') {
		enemySpawnCounter++;
		if (enemySpawnCounter >= enemySpawnInterval) {
			//get if enemy2 is on the map.
			var enemy2Exists = enemies.some(function (e) {
				return e instanceof Enemy2;
			});
			var enemy;
			var playerScore = LK.getScore();
			if (!enemy2Exists && playerScore > 10 && Math.random() < 0.4) {
				enemy = new Enemy4();
				enemy.x = 2048;
				enemy.y = 2732 / 2;
				enemies.push(enemy);
				enemyContainer.addChild(enemy);
			} else {
				enemy = new Enemy();
				enemy.x = 2048;
				enemy.y = 2732 / 2;
				enemies.push(enemy);
				enemyContainer.addChild(enemy);
				if (playerScore > 3 && Math.random() < 0.4) {
					enemy.images['horns'].visible = true;
				}
			}
			if (!enemy2Exists && playerScore >= 5) {
				addEnemy2(game, enemies);
			}
			// Randomize the spawn interval for the next enemy
			enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
			enemySpawnCounter = 0;
		}
	}
	// Check collision between player bodyCollider and bullets
	for (var k = bulletContainer.children.length - 1; k >= 0; k--) {
		var bullet = bulletContainer.children[k];
		if (bullet.intersects(player.bodyCollider)) {
			// Send BULLET_HIT event to player
			player.sendEvent(GameEvents.BULLET_HIT, bullet);
			// bullet.destroy();
			// bulletContainer.removeChild(bullet);
		}
	}
	// Update enemies
	for (var j = enemies.length - 1; j >= 0; j--) {
		enemies[j].update();
		// Skip collision checks if player is in the dead state
		if (player.state === 'dead') {
			continue;
		}
		//remove the enemies marked for removal.
		if (enemies[j].removeFromArray == true) {
			enemies[j].destroy();
			enemies.splice(j, 1);
			continue;
		}
		//skip collisions if enemy is dead.
		if (enemies[j].state === 'dead') {
			continue;
		}
		// Check collision between player bodyCollider and enemy headCollider
		if (player.bodyCollider.intersects(enemies[j].headCollider)) {
			onEnemyDeath(enemies[j]); // Call onEnemyDeath function
			// Send COLLISION_ENEMY_HEAD event to player
			player.sendEvent(GameEvents.COLLISION_ENEMY_HEAD, enemies[j]);
			enemies[j].sendEvent(GameEvents.COLLISION_ENEMY_HEAD, player);
			continue;
		}
		// Check collision between enemy bodyCollider and player bodyCollider
		if (enemies[j].bodyCollider.intersects(player.bodyCollider)) {
			// Send COLLISION_ENEMY_BODY event to player
			player.sendEvent(GameEvents.COLLISION_ENEMY_BODY, enemies[j]);
			enemies[j].sendEvent(GameEvents.COLLISION_PLAYER_BODY, player);
			return;
		}
	}
};
// Handle player jump
game.down = function (x, y, obj) {
	if (y > 2732 / 2 && x < 2048 / 2) {
		// Check if click is on the left half
		player.jump();
	} else if (y > 2732 / 2) {
		// Otherwise, it's on the right half
		player.attack();
	}
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	playerDeaths: 0,
	monsterDeaths: 0
});
/**** 
* Classes
****/ 
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	var bulletGraphics = self.attachAsset('enemy2_horn', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = 10;
	self.update = function () {
		self.x -= self.speed;
		if (self.speed < 0) {
			self.rotation += 0.1; // Add spinning effect
			// Check collision with enemies
			for (var i = 0; i < enemyContainer.children.length; i++) {
				var enemy = enemyContainer.children[i];
				if (self.intersects(enemy.bodyCollider)) {
					enemy.sendEvent(GameEvents.BULLET_HIT, self);
				}
			}
		}
		if (self.x < 0) {
			self.destroy();
		} else if (self.x > 2048 + 200) {
			self.destroy();
		}
	};
});
var Enemy = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy1_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('enemy1_walk1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('enemy1_walk2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horns": LK.getAsset('enemy1_horns', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	//replacing the asset ref with the image added.
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	//configure the animation frames.
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'dead': new FrameAnimation([self.images.dead], 100)
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	// Add a red rectangle to the enemy's head
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 100
	});
	self.headCollider.y = -enemyGraphics.height / 4; // Position the rectangle on the enemy's head
	self.headCollider.alpha = 0;
	// Add a bodyCollider rectangle to the bottom part of the enemy
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00
	});
	self.bodyCollider.y = enemyGraphics.height / 4; // Position the rectangle on the enemy's body
	self.bodyCollider.alpha = 0;
	self.speed = 5;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
			},
			onExit: function onExit() {
				console.log('Enemy stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (self.hasHorns() == false) {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				tween(monsterDeathsText.scale, {
					x: 1.5,
					y: 1.5
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(monsterDeathsText.scale, {
							x: 1,
							y: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6 // 30 degrees in radians
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	// Function to check if the enemy has visible horns
	self.hasHorns = function () {
		return self.images['horns'] && self.images['horns'].visible;
	};
});
var Enemy2 = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy2_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('enemy2_walk1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('enemy2_walk2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"attack1": LK.getAsset('enemy2_attack1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"attack2": LK.getAsset('enemy2_attack2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horn": LK.getAsset('enemy2_horn', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle1": LK.getAsset('enemy2_idle1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle2": LK.getAsset('enemy2_idle2', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'dead': new FrameAnimation([self.images.dead], 100),
		'idle': new FrameAnimation([self.images.idle1, self.images.idle2], 300),
		// New idle animation
		'attack': new FrameAnimation([self.images.attack1, self.images.attack2], 300) // New attack animation
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 100
	});
	self.headCollider.y = -enemyGraphics.height / 4;
	self.headCollider.alpha = 0;
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00
	});
	self.bodyCollider.y = enemyGraphics.height / 4;
	self.bodyCollider.alpha = 0;
	self.speed = 5;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy2 starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
				// Transition to attack state when reaching 2/3rd of the screen
				if (self.lastX > 2048 * (2 / 3) && self.x <= 2048 * (2 / 3)) {
					self.setState('attack');
				}
				self.lastX = self.x;
			},
			onExit: function onExit() {
				console.log('Enemy2 stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					self.setState('dead');
				}
			}
		},
		attack: {
			// New attack state
			onEnter: function onEnter() {
				console.log('Enemy2 starts attacking');
				self.anims['idle'].play(); // Play idle animation
				self.attackTimer = 500; // Set attack timer
			},
			onUpdate: function onUpdate() {
				if (self.attackTimer > 0) {
					self.attackTimer--; // Decrease attack timer
				} else {
					self.anims['idle'].stop(); // Stop idle animation
					self.anims['attack'].play(); // Play attack animation
					if (isSoundOn) {
						LK.getSound('roar').play();
					}
					self.attackTimer = 1000;
				}
			},
			onExit: function onExit() {
				console.log('Enemy2 stops attacking');
				self.anims['idle'].stop();
				self.anims['attack'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in attack state: ', event, data);
				if (event === GameEvents.ANIM_FINISHED && data == self.anims['attack']) {
					// Shoot a bullet at the player using enemy_horn image
					var bullet = new Bullet(self.images.horn);
					bullet.x = self.x;
					bullet.y = self.y;
					bulletContainer.addChild(bullet);
					self.anims['attack'].stop();
					self.anims['idle'].play();
					self.attackTimer = 500;
				} else if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					self.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		// End of new attack state
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy2 has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	// Function to check if the enemy has visible horns
	self.hasHorns = function () {
		return self.images['horn'] && self.images['horn'].visible;
	};
});
var Enemy4 = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"dead": LK.getAsset('enemy4_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle1": LK.getAsset('enemy4_idle1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"idle2": LK.getAsset('enemy4_idle2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"horns": LK.getAsset('enemy1_horns', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	self.anims = {
		'walk': new FrameAnimation([self.images.idle1, self.images.idle2], 300),
		'dead': new FrameAnimation([self.images.dead], 100)
	};
	var enemyGraphics = {
		height: 300,
		width: 300
	};
	self.headCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 120,
		height: 100,
		x: -200,
		y: 150 - 50
	});
	self.headCollider.alpha = 0;
	self.bodyCollider = self.attachAsset('enemyRectangle', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 200,
		height: 200,
		x: 0
	});
	self.bodyCollider.y = enemyGraphics.height / 4;
	self.bodyCollider.alpha = 0;
	self.speed = 8;
	self.state;
	self.removeFromArray = false;
	self.states = {
		moving: {
			onEnter: function onEnter() {
				console.log('Enemy4 starts moving');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {
				self.x -= self.speed;
				if (self.x < -500) {
					self.setState('dead');
				}
			},
			onExit: function onExit() {
				console.log('Enemy4 stops moving');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in moving state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					data.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					// Do nothing for Enemy4 when hit by a bullet
				} else if (event == GameEvents.COLLISION_PLAYER_BODY) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Enemy4 has died');
				if (isSoundOn) {
					LK.getSound('bounce').play();
				}
				monsterDeaths++;
				storage.monsterDeaths = monsterDeaths;
				onEnemyDeath(self);
				self.anims['dead'].play();
				self.speed = 0;
				var y_val = self.y - Math.random() * 150;
				var x_val = self.x + 100 + Math.random() * 200;
				tween(self, {
					x: x_val,
					y: y_val,
					rotation: Math.PI / 6
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 500,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								self.removeFromArray = true;
							}
						});
					}
				});
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting dead state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in dead state: ', event, data);
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.setState('moving');
	self.hasHorns = function () {
		return true;
	};
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
	var self = Container.call(this);
	self.images = {
		"idle": LK.getAsset('player', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"jump": LK.getAsset('player_jump', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk1": LK.getAsset('walk_1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		"walk2": LK.getAsset('walk_2', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'dead': LK.getAsset('player_dead', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'attack1': LK.getAsset('player_attack1', {
			anchorX: 0.5,
			anchorY: 0.5
		}),
		'attack2': LK.getAsset('player_attack2', {
			anchorX: 0.5,
			anchorY: 0.5
		})
	};
	self.speed = 5;
	self.jumpHeight = 40;
	self.isJumping = false;
	self.velocityY = 0;
	self.state;
	//replacing the asset ref with the image added.
	for (var img_name in self.images) {
		var img = self.addChild(self.images[img_name]);
		img.visible = false;
		self.images[img_name] = img;
	}
	// Add a bodyCollider rectangle to the player character
	self.bodyCollider = self.attachAsset('collider', {
		anchorX: 0.5,
		anchorY: 0.5,
		width: 150,
		height: 150,
		color: 0xffff00,
		// Yellow color
		alpha: 0
	});
	self.bodyCollider.y = self.images.idle.height / 4; // Position the rectangle on the player's body
	//configure the animation frames.
	self.anims = {
		'walk': new FrameAnimation([self.images.walk1, self.images.walk2], 300),
		'jump': new FrameAnimation([self.images.jump], 100),
		'idle': [self.images.idle],
		'dead': new FrameAnimation([self.images.dead], 100),
		'attack': new FrameAnimation([self.images.jump, self.images.attack1, self.images.attack2, self.images.idle], 200)
	};
	self.states = {
		idle: {
			onEnter: function onEnter() {
				console.log('Entering idle state');
				self.anims['walk'].play();
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting idle state');
				self.anims['walk'].stop();
			},
			onEvent: function onEvent(event, data) {
				if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (data instanceof Enemy4) {
						//do nothing.
					} else {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		},
		dead: {
			onEnter: function onEnter() {
				console.log('Player has died');
				if (isSoundOn) {
					LK.getSound('player_hit').play();
				}
				playerDeaths++;
				storage.playerDeaths = playerDeaths;
				playerDeathsText.setText('' + playerDeaths);
				tween(playerDeathsText.scale, {
					x: 1.5,
					y: 1.5
				}, {
					duration: 100,
					easing: tween.easeOut,
					onFinish: function onFinish() {
						tween(playerDeathsText.scale, {
							x: 1,
							y: 1
						}, {
							duration: 100,
							easing: tween.easeIn
						});
					}
				});
				tween(self, {
					y: self.y - 400,
					rotation: -Math.PI / 6 // 30 degrees in radians
				}, {
					duration: 250,
					easing: tween.expoOut,
					onFinish: function onFinish() {
						tween(self, {
							y: 2732 + 300
						}, {
							duration: 1000,
							easing: tween.expoIn,
							onFinish: function onFinish() {
								LK.showGameOver();
							}
						});
					}
				});
				// Stop all enemies from moving
				for (var j = 0; j < enemies.length; j++) {
					enemies[j].speed = 0;
				}
				self.anims['dead'].play();
				// Additional logic for death state can be added here
			},
			onUpdate: function onUpdate() {},
			onExit: function onExit() {
				console.log('Exiting death state');
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in death state: ', event, data);
			}
		},
		attack: {
			onEnter: function onEnter() {
				console.log('Player is attacking');
				if (isSoundOn) {
					LK.getSound('punch_swing').play();
				}
				self.anims['attack'].play();
			},
			onUpdate: function onUpdate() {
				// Attack logic can be added here
			},
			onExit: function onExit() {
				console.log('Exiting attack state');
				self.anims['attack'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in attack state: ', event, data);
				if (event === GameEvents.ANIM_FINISHED) {
					self.setState('idle');
				} else if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (data.hasHorns()) {
						self.setState('dead');
					} else {
						data.setState('dead');
					}
				} else if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (isSoundOn) {
						LK.getSound('punch').play();
					}
					data.setState('dead');
				} else if (event === GameEvents.BULLET_HIT) {
					if (data.speed > 0) {
						data.speed = -data.speed;
						if (isSoundOn) {
							LK.getSound('punch').play();
						}
					} // Reverse bullet direction
				}
			}
		},
		jumping: {
			onEnter: function onEnter() {
				if (!self.isJumping) {
					if (isSoundOn) {
						LK.getSound('jump').play();
					}
					self.isJumping = true;
					self.velocityY = -self.jumpHeight;
					self.anims['jump'].play();
					console.log("jump Started");
				}
			},
			onUpdate: function onUpdate() {
				if (self.isJumping) {
					self.y += self.velocityY;
					self.velocityY += 1.4; // Increase gravity effect to make descent twice as fast 
					if (self.y >= 2732 / 2) {
						// Ground level
						self.y = 2732 / 2;
						self.isJumping = false;
						self.velocityY = 0;
						self.setState('idle');
					}
				}
			},
			onExit: function onExit() {
				console.log('Exiting jumping state');
				self.anims['jump'].stop();
			},
			onEvent: function onEvent(event, data) {
				console.log('Event received in jumping state: ', event, data);
				if (event === GameEvents.COLLISION_ENEMY_HEAD) {
					if (data.hasHorns()) {
						self.setState('dead');
					} else {
						// Add upward velocity to player
						player.velocityY = -player.jumpHeight / 2;
						player.isJumping = true;
						if (isSoundOn) {
							LK.getSound('bounce').play();
						}
					}
				} else if (event === GameEvents.COLLISION_ENEMY_BODY) {
					if (data instanceof Enemy4) {
						// Add upward velocity to player
						player.velocityY = -player.jumpHeight / 2;
						player.isJumping = true;
						if (isSoundOn) {
							LK.getSound('bounce').play();
						}
					} else {
						self.setState('dead');
					}
				} else if (event === GameEvents.BULLET_HIT) {
					self.setState('dead');
				}
			}
		}
	};
	self.setState = function (state) {
		if (self.state) {
			self.states[self.state].onExit();
		}
		self.state = state;
		self.states[self.state].onEnter();
	};
	self.sendEvent = function (event, data) {
		if (self.state) {
			self.states[self.state].onEvent(event, data);
		}
	};
	self.update = function () {
		self.states[self.state].onUpdate();
	};
	self.attack = function () {
		if (self.state !== 'jumping' && self.state !== 'dead') {
			self.setState('attack');
		}
	};
	self.jump = function () {
		if (self.state !== 'jumping' && self.state !== 'dead') {
			self.setState('jumping');
		}
	};
	//call the initial state.
	self.setState('idle');
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x87CEEB // Sky blue background
});
/**** 
* Game Code
****/ 
function onEnemyDeath(enemy) {
	LK.setScore(LK.getScore() + 1);
	scoreText.setText(LK.getScore());
	// Increment the count of enemies killed by the player
	enemiesKilledByPlayer++;
	monsterDeathsText.setText('' + monsterDeaths);
	// Check if the score is 8 or above and 5 enemies have been killed
	if (LK.getScore() >= 8 && enemiesKilledByPlayer % 5 === 0) {
		// Spawn Enemy4
		var enemy4 = new Enemy4();
		enemy4.x = 2048;
		enemy4.y = 2732 / 2;
		enemies.push(enemy4);
		enemyContainer.addChild(enemy4);
	}
}
if (typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true) {
	LK.playMusic('platformer_music'); // Start playing background music
}
var GameEvents = {
	COLLISION_ENEMY_HEAD: 0,
	COLLISION_ENEMY_BODY: 1,
	COLLISION_PLAYER_BODY: 2,
	ANIM_FINISHED: 3,
	BULLET_HIT: 4 // New event for when player is hit by enemy bullet
};
// Initialize death counters from storage
var playerDeaths = storage.playerDeaths;
var monsterDeaths = storage.monsterDeaths;
// Track the number of enemies killed by the player
var enemiesKilledByPlayer = 0;
// Define a class for frame animations
var FrameAnimation = function FrameAnimation(assets, frameRate) {
	var self = this;
	self.assets = assets;
	self.frameRate = frameRate;
	self.currentFrame = 0;
	self.isPlaying = false;
	self.interval = null;
	// Function to show the current frame
	self.showFrame = function () {
		for (var i = 0; i < self.assets.length; i++) {
			self.assets[i].visible = false;
		}
		self.assets[self.currentFrame].visible = true;
	};
	// Play the animation
	self.play = function () {
		if (!self.isPlaying) {
			self.isPlaying = true;
			self.showFrame();
			if (self.assets.length > 1) {
				self.interval = LK.setInterval(function () {
					self.currentFrame = (self.currentFrame + 1) % self.assets.length;
					self.showFrame();
					if (self.currentFrame === 0) {
						// Send ANIM_FINISHED event when animation completes a cycle
						self.assets[0].parent.sendEvent(GameEvents.ANIM_FINISHED, self);
					}
				}, self.frameRate);
			}
		}
	};
	// Pause the animation
	self.pause = function () {
		if (self.isPlaying) {
			clearInterval(self.interval);
			self.isPlaying = false;
		}
	};
	// Resume the animation
	self.resume = function () {
		if (!self.isPlaying) {
			self.play();
		}
	};
	// Stop the animation
	self.stop = function () {
		LK.clearInterval(self.interval);
		self.isPlaying = false;
		self.currentFrame = 0;
		for (var i = 0; i < self.assets.length; i++) {
			self.assets[i].visible = false;
		}
	};
};
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0.5,
	anchorY: 0.5
}));
background.x = 2048 / 2;
background.y = 2732 / 2 - 550;
// Initialize player
var player = game.addChild(new Player());
player.x = 150; // Position player on the left side of the screen
player.y = 2732 / 2;
// Initialize enemy container
var enemyContainer = new Container();
game.addChild(enemyContainer);
// Initialize enemies array
var enemies = [];
// Initialize bullet container
var bulletContainer = new Container();
game.addChild(bulletContainer);
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Create a new Text2 object to display the score
var scoreText = new Text2('0', {
	size: 100,
	fill: 0xFFFFFF
});
// Create a GUI container
var guiContainer = new Container();
game.addChild(guiContainer);
// Add the score text to the GUI container at the top center of the screen
guiContainer.addChild(scoreText);
scoreText.x = 2048 / 2;
scoreText.y = 20; // Adjusted to align with other top elements
scoreText.anchor.set(0.5, 0); // Center the score text horizontally
// Add a jump button to the bottom of the screen in the GUI container
var jumpButton = LK.getAsset('jump_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 400,
	height: 400
});
jumpButton.x = 2048 / 4;
jumpButton.y = 2732 - jumpButton.height / 2 - 300;
guiContainer.addChild(jumpButton);
// Add an attack button to the bottom of the screen in the GUI container
var attackButton = LK.getAsset('attack_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 400,
	height: 400
});
attackButton.x = 3 * 2048 / 4;
attackButton.y = 2732 - attackButton.height / 2 - 300;
guiContainer.addChild(attackButton);
// Add event listeners to flash buttons black on mouse or touch down
jumpButton.down = function (x, y, obj) {
	LK.effects.flashObject(jumpButton, 0x000000, 500);
	player.jump();
};
attackButton.down = function (x, y, obj) {
	LK.effects.flashObject(attackButton, 0x000000, 500);
	player.attack();
};
// Add a settings button to the top right of the screen
var settingsButton = LK.getAsset('settings_button', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
settingsButton.x = 2048 - settingsButton.width / 2 - 50;
settingsButton.y = settingsButton.height / 2 + 50;
guiContainer.addChild(settingsButton);
// Add a music button below the settings button, initially hidden
var musicButton = LK.getAsset('music_on', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
musicButton.x = settingsButton.x;
musicButton.y = settingsButton.y + settingsButton.height / 2 + musicButton.height / 2 + 30;
guiContainer.addChild(musicButton);
music_off = LK.getAsset('music_off', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200,
	x: musicButton.x,
	y: musicButton.y,
	visible: false
});
guiContainer.addChild(music_off);
// Add a sound button below the music button, initially hidden
var soundButton = LK.getAsset('sound_on', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200
});
soundButton.x = musicButton.x;
soundButton.y = musicButton.y + musicButton.height + 30;
guiContainer.addChild(soundButton);
var sound_off = LK.getAsset('sound_off', {
	anchorX: 0.5,
	anchorY: 0.5,
	width: 200,
	height: 200,
	x: soundButton.x,
	y: soundButton.y,
	visible: false
});
guiContainer.addChild(sound_off);
// Initially hide music and sound buttons
musicButton.visible = false;
music_off.visible = false;
soundButton.visible = false;
sound_off.visible = false;
// Toggle music and sound button visibility when settings button is pressed
settingsButton.down = function (x, y, obj) {
	var newVisible = !musicButton.visible;
	musicButton.visible = newVisible;
	music_off.visible = newVisible && !isMusicPlaying;
	soundButton.visible = newVisible;
	sound_off.visible = newVisible && !isSoundOn;
};
// Add event listener to toggle music on button press
var isMusicPlaying = typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true; // Initialize music state from storage
// Set initial visibility for music and sound buttons based on storage and settings toggle
var isMusicPlaying = typeof storage.isMusicPlaying === "boolean" ? storage.isMusicPlaying : true; // Initialize music state from storage
var isSoundOn = typeof storage.isSoundOn === "boolean" ? storage.isSoundOn : true; // Initialize sound state from storage
// When music button is pressed, toggle music and update icons
musicButton.down = function (x, y, obj) {
	LK.effects.flashObject(musicButton, 0x000000, 500);
	LK.effects.flashObject(music_off, 0x000000, 500);
	if (isMusicPlaying) {
		LK.stopMusic(); // Stop playing background music
		music_off.visible = true;
		musicButton.visible = true;
	} else {
		LK.playMusic('platformer_music'); // Start playing background music
		music_off.visible = false;
		musicButton.visible = true;
	}
	isMusicPlaying = !isMusicPlaying; // Toggle music state
	storage.isMusicPlaying = isMusicPlaying; // Persist to storage
};
// When sound button is pressed, toggle sound and update icons
soundButton.down = function (x, y, obj) {
	LK.effects.flashObject(soundButton, 0x000000, 500);
	LK.effects.flashObject(sound_off, 0x000000, 500);
	if (isSoundOn) {
		sound_off.visible = true;
		soundButton.visible = true;
	} else {
		sound_off.visible = false;
		soundButton.visible = true;
	}
	isSoundOn = !isSoundOn;
	storage.isSoundOn = isSoundOn; // Persist to storage
};
// Add the score_display image to the GUI container at the top center of the screen
var scoreDisplayImage = LK.getAsset('score_display', {
	anchorX: 0.5,
	anchorY: 0.0
});
scoreDisplayImage.x = 2048 / 2;
scoreDisplayImage.y = 0; // Align to the top of the screen
guiContainer.addChild(scoreDisplayImage);
// Create Text2 objects to display player and monster deaths
var playerDeathsText = new Text2('' + playerDeaths, {
	size: 72,
	fill: 0xFFFFFF,
	anchorX: 0.5,
	anchorY: 0.5,
	font: "VAG Rounded" // More rounded and thick font 
});
var monsterDeathsText = new Text2('' + monsterDeaths, {
	size: 72,
	fill: 0xFFFFFF,
	anchorX: 0.5,
	anchorY: 0.5,
	font: "VAG Rounded" // More rounded and thick font 
});
// Position the death counters at the top of the screen
playerDeathsText.x = scoreDisplayImage.x + 150;
playerDeathsText.y = 190;
monsterDeathsText.x = scoreDisplayImage.x - 280;
monsterDeathsText.y = 190;
// Add the death counters to the GUI container
guiContainer.addChild(playerDeathsText);
guiContainer.addChild(monsterDeathsText);
function addEnemy2(game, enemies) {
	enemy = new Enemy2();
	enemy.x = 2048;
	enemy.y = 2732 / 2;
	enemies.push(enemy);
	enemyContainer.addChild(enemy);
}
// Handle game updates
game.update = function () {
	player.update();
	// Spawn enemies
	if (player.state !== 'dead') {
		enemySpawnCounter++;
		if (enemySpawnCounter >= enemySpawnInterval) {
			//get if enemy2 is on the map.
			var enemy2Exists = enemies.some(function (e) {
				return e instanceof Enemy2;
			});
			var enemy;
			var playerScore = LK.getScore();
			if (!enemy2Exists && playerScore > 10 && Math.random() < 0.4) {
				enemy = new Enemy4();
				enemy.x = 2048;
				enemy.y = 2732 / 2;
				enemies.push(enemy);
				enemyContainer.addChild(enemy);
			} else {
				enemy = new Enemy();
				enemy.x = 2048;
				enemy.y = 2732 / 2;
				enemies.push(enemy);
				enemyContainer.addChild(enemy);
				if (playerScore > 3 && Math.random() < 0.4) {
					enemy.images['horns'].visible = true;
				}
			}
			if (!enemy2Exists && playerScore >= 5) {
				addEnemy2(game, enemies);
			}
			// Randomize the spawn interval for the next enemy
			enemySpawnInterval = Math.floor(Math.random() * 150) + 50;
			enemySpawnCounter = 0;
		}
	}
	// Check collision between player bodyCollider and bullets
	for (var k = bulletContainer.children.length - 1; k >= 0; k--) {
		var bullet = bulletContainer.children[k];
		if (bullet.intersects(player.bodyCollider)) {
			// Send BULLET_HIT event to player
			player.sendEvent(GameEvents.BULLET_HIT, bullet);
			// bullet.destroy();
			// bulletContainer.removeChild(bullet);
		}
	}
	// Update enemies
	for (var j = enemies.length - 1; j >= 0; j--) {
		enemies[j].update();
		// Skip collision checks if player is in the dead state
		if (player.state === 'dead') {
			continue;
		}
		//remove the enemies marked for removal.
		if (enemies[j].removeFromArray == true) {
			enemies[j].destroy();
			enemies.splice(j, 1);
			continue;
		}
		//skip collisions if enemy is dead.
		if (enemies[j].state === 'dead') {
			continue;
		}
		// Check collision between player bodyCollider and enemy headCollider
		if (player.bodyCollider.intersects(enemies[j].headCollider)) {
			onEnemyDeath(enemies[j]); // Call onEnemyDeath function
			// Send COLLISION_ENEMY_HEAD event to player
			player.sendEvent(GameEvents.COLLISION_ENEMY_HEAD, enemies[j]);
			enemies[j].sendEvent(GameEvents.COLLISION_ENEMY_HEAD, player);
			continue;
		}
		// Check collision between enemy bodyCollider and player bodyCollider
		if (enemies[j].bodyCollider.intersects(player.bodyCollider)) {
			// Send COLLISION_ENEMY_BODY event to player
			player.sendEvent(GameEvents.COLLISION_ENEMY_BODY, enemies[j]);
			enemies[j].sendEvent(GameEvents.COLLISION_PLAYER_BODY, player);
			return;
		}
	}
};
// Handle player jump
game.down = function (x, y, obj) {
	if (y > 2732 / 2 && x < 2048 / 2) {
		// Check if click is on the left half
		player.jump();
	} else if (y > 2732 / 2) {
		// Otherwise, it's on the right half
		player.attack();
	}
};
 Single 2D Mario Character. In-Game asset. 2d. Blank background.
 
 
 
 
 
 
 
 
 
 
 
 monster in flat shading style. Monster has chopped off horns and has lazy eye with scary teeth. think black borders Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
 
 
 
 
 
 
 
 Sprite sheet of a monster enemy that has crucked teeth and a crazy eye. The sprite sheet has different frames of the enemy doing actions like run , attack , dodge, hurt and die.. High contrast