User prompt
Player can only shoot the villains
User prompt
When blue semibot See's player blue semibot helps player defeat the villains
User prompt
Make the blue semibot kill the villain make the blue semibot Chase the villain
User prompt
Please fix the bug: 'LK.setPersistent is not a function' in or related to this line: 'LK.setPersistent(semibot);' Line Number: 1246
User prompt
Can you make the blue semibot stay
User prompt
Add blue semibot character
User prompt
Rename Taxmanlcon to taxman and stop calling tax man player it's just the villain there's two villains
User prompt
Make the taxman come after the player
User prompt
The taxman is not the player the taxman is the enemy not Taxmanlcon
User prompt
Add the taxman asset
User prompt
Make a weapon for the player
User prompt
Main villain ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'playerHealthText.style.fill = player.health <= 20 ? 0xe74c3c : player.health <= 50 ? 0xf39c12 : 0x00FF00;' Line Number: 946
User prompt
Add a player
User prompt
Add player
User prompt
Add semibot and taxman
User prompt
I need a semibot player ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Repo bot player
Code edit (1 edits merged)
Please save this source code
User prompt
R.E.P.O. - Repository Emergency Protocol Override
Initial prompt
Make r.e.p.o.
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
/**** 
* Classes
****/ 
var Bullet = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.speedX = 0;
	self.speedY = -10; // Default upward
	self.damage = 10;
	self.range = 500;
	self.distanceTraveled = 0;
	self.lastX = 0;
	self.lastY = 0;
	// Create bullet graphic
	var bulletGraphic = self.attachAsset('dataPacket', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.5,
		scaleY: 0.5
	});
	bulletGraphic.tint = 0x00ffff; // Cyan color for bullet
	// Update method
	self.update = function () {
		// Store last position
		self.lastX = self.x;
		self.lastY = self.y;
		// Move bullet
		self.x += self.speedX;
		self.y += self.speedY;
		// Calculate distance traveled
		var dx = self.x - self.lastX;
		var dy = self.y - self.lastY;
		self.distanceTraveled += Math.sqrt(dx * dx + dy * dy);
		// Rotate in direction of movement
		self.rotation = Math.atan2(self.speedY, self.speedX);
		// Store if this bullet has already hit something
		self.isDestroyed = false;
	};
	return self;
});
var DataPacket = Container.expand(function () {
	var self = Container.call(this);
	// Color constants
	var COLORS = [0x3498db, 0xe74c3c, 0xf39c12, 0x2ecc71, 0x9b59b6];
	var COLOR_NAMES = ['blue', 'red', 'orange', 'green', 'purple'];
	// Properties
	self.colorIndex = 0;
	self.colorName = '';
	self.isBeingDragged = false;
	self.lastX = 0;
	self.lastY = 0;
	self.lastWasIntersecting = false;
	self.matchedNode = null;
	// Create packet graphic
	var packetGraphic = self.attachAsset('dataPacket', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Set random color
	self.setRandomColor = function () {
		self.colorIndex = Math.floor(Math.random() * COLORS.length);
		self.colorName = COLOR_NAMES[self.colorIndex];
		packetGraphic.tint = COLORS[self.colorIndex];
	};
	// Down event for dragging
	self.down = function (x, y, obj) {
		self.isBeingDragged = true;
		// Bring to front
		if (self.parent) {
			self.parent.removeChild(self);
			self.parent.addChild(self);
		}
	};
	// Update method called every tick
	self.update = function () {
		// Track position for intersection detection
		self.lastX = self.x;
		self.lastY = self.y;
	};
	// Initialize with random color
	self.setRandomColor();
	return self;
});
var ErrorBlock = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.lifespan = 10 * 60; // 10 seconds at 60fps
	// Create block graphic
	var blockGraphic = self.attachAsset('errorBlock', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Update method
	self.update = function () {
		self.lifespan--;
		// Fade out near end of lifespan
		if (self.lifespan < 60) {
			self.alpha = self.lifespan / 60;
		}
		// Rotate slowly
		self.rotation += 0.01;
	};
	return self;
});
var Player = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.speed = 5;
	self.health = 100;
	self.maxHealth = 100;
	self.score = 0;
	self.lastX = 0;
	self.lastY = 0;
	self.isMoving = false;
	self.lastDirection = 'right';
	// Create player graphic
	var playerGraphic = self.attachAsset('dataPacket', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Set player appearance
	playerGraphic.tint = 0xffffff; // White color
	playerGraphic.scaleX = 1.3;
	playerGraphic.scaleY = 1.3;
	// Health bar background
	var healthBarBg = self.attachAsset('stabilityBarBg', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 0.3
	});
	healthBarBg.y = -60;
	// Health bar
	var healthBar = self.attachAsset('stabilityBar', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.5,
		scaleY: 0.2
	});
	healthBar.y = -60;
	healthBar.x = -healthBarBg.width / 2;
	// Set health level
	self.setHealth = function (amount) {
		self.health = Math.max(0, Math.min(self.maxHealth, amount));
		var healthPercentage = self.health / self.maxHealth;
		healthBar.scaleX = 1.5 * healthPercentage;
		// Update bar color based on health
		if (self.health <= 20) {
			healthBar.tint = 0xe74c3c; // Critical - red
		} else if (self.health <= 50) {
			healthBar.tint = 0xf39c12; // Warning - orange
		} else {
			healthBar.tint = 0x27ae60; // Normal - green
		}
	};
	// Move player in specified direction
	self.move = function (dx, dy) {
		self.isMoving = true;
		// Determine direction
		if (Math.abs(dx) > Math.abs(dy)) {
			self.lastDirection = dx > 0 ? 'right' : 'left';
		} else {
			self.lastDirection = dy > 0 ? 'down' : 'up';
		}
		// Update position
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += dx * self.speed;
		self.y += dy * self.speed;
		// Keep player within game bounds
		self.x = Math.max(50, Math.min(2048 - 50, self.x));
		self.y = Math.max(150, Math.min(2732 - 150, self.y));
		// Move weapon with player
		if (playerWeapon) {
			playerWeapon.x = self.x;
			playerWeapon.y = self.y - 30;
		}
	};
	// Collect packet
	self.collectPacket = function (packet) {
		// Visual feedback
		tween(self, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 200,
			onFinish: function onFinish() {
				tween(self, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
		// Increase score
		self.score += 10;
		// Heal player slightly
		self.setHealth(self.health + 5);
		return self.score;
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.setHealth(self.health - amount);
		// Visual feedback
		LK.effects.flashObject(self, 0xff0000, 500);
		return self.health;
	};
	// Update method
	self.update = function () {
		// Reset moving state each frame
		self.isMoving = false;
		// Pulse effect when health is low
		if (self.health < 30) {
			var pulseAmount = Math.sin(LK.ticks * 0.1) * 0.1 + 1;
			playerGraphic.scale.set(pulseAmount * 1.3, pulseAmount * 1.3);
		}
		// Store last position for collision detection
		self.lastX = self.x;
		self.lastY = self.y;
	};
	// Initialize health
	self.setHealth(self.health);
	return self;
});
var PowerUp = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.type = 'slowTime'; // Default type
	self.isBeingDragged = false;
	self.lastX = 0;
	self.lastY = 0;
	self.lastWasIntersecting = false;
	// Create power-up graphic
	var powerUpGraphic = self.attachAsset('powerUp', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Power-up types and their colors
	var TYPES = ['slowTime', 'clearPath', 'stabilityBoost'];
	var TYPE_COLORS = [0xf39c12, 0x9b59b6, 0x3498db];
	// Set type
	self.setType = function (typeIndex) {
		self.type = TYPES[typeIndex];
		powerUpGraphic.tint = TYPE_COLORS[typeIndex];
	};
	// Down event for dragging
	self.down = function (x, y, obj) {
		self.isBeingDragged = true;
		// Bring to front
		if (self.parent) {
			self.parent.removeChild(self);
			self.parent.addChild(self);
		}
	};
	// Update method
	self.update = function () {
		// Pulse effect
		var pulseAmount = Math.sin(LK.ticks * 0.1) * 0.1 + 1;
		self.scale.set(pulseAmount, pulseAmount);
		// Track position
		self.lastX = self.x;
		self.lastY = self.y;
	};
	// Set random type
	self.setType(Math.floor(Math.random() * TYPES.length));
	return self;
});
var SemibotPlayer = Container.expand(function () {
	var self = Container.call(this);
	// Bot properties
	self.active = true;
	self.intelligence = 0.7; // 0-1 scale of bot intelligence
	self.lastActionTime = 0;
	self.actionDelay = 60; // Frames between actions
	self.target = null;
	// Create semibot graphic
	var botGraphic = self.attachAsset('blueBot', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.2,
		scaleY: 1.2
	});
	botGraphic.tint = 0x3498db; // Blue color for semibot
	// Toggle bot activity
	self.toggle = function () {
		self.active = !self.active;
		return self.active;
	};
	// Find optimal move - returns best packet and target node or villain to attack
	self.findOptimalMove = function () {
		// ALWAYS prioritize attacking the villain if player is visible
		if (player && villain && villain.active) {
			return {
				attackVillain: true
			};
		}
		// Fall back to normal packet matching if villain isn't present
		if (!packets.length || !storageNodes.length) return null;
		// Only act if we have a good match
		var bestMatch = null;
		var highestScore = -1;
		for (var i = 0; i < packets.length; i++) {
			var packet = packets[i];
			// Skip packets being dragged by player
			if (packet.isBeingDragged) continue;
			for (var j = 0; j < storageNodes.length; j++) {
				var node = storageNodes[j];
				var score = 0;
				// Color match is most important
				if (packet.colorIndex === node.colorIndex) {
					score += 100;
					// Prioritize continuing chains
					if (lastSuccessfulChain === packet.colorIndex) {
						score += 50 * chainCount;
					}
					// Adjust for difficulty based on distance and position
					score -= Math.abs(packet.x - node.x) * 0.05;
					score -= packet.y * 0.1; // Prioritize packets farther down
					// Apply intelligence factor - sometimes make suboptimal choices
					if (Math.random() > self.intelligence) {
						score *= Math.random() * 0.5;
					}
					if (score > highestScore) {
						highestScore = score;
						bestMatch = {
							packet: packet,
							node: node
						};
					}
				}
			}
		}
		return bestMatch;
	};
	// Chase and attack villain
	self.chaseVillain = function () {
		if (!villain || !villain.active) return false;
		// Check if player is present - semibot only helps when player is around
		if (!player) return false;
		// Visual feedback - move semibot toward villain more quickly
		tween(self, {
			x: villain.x,
			y: villain.y - 50
		}, {
			duration: 200,
			// Faster movement
			easing: tween.easeOutQuad,
			onFinish: function onFinish() {
				// Attack villain if close enough
				if (self.intersects(villain)) {
					// Deal increased damage to villain when helping player
					villain.takeDamage(30);
					// Flash semibot when attacking
					LK.effects.flashObject(self, 0x00ffff, 300);
					// If villain is defeated, prevent behavior from running again
					if (villain.health <= 0) {
						villain.active = false;
						// Add visual celebration effect for player and semibot defeating villain
						LK.effects.flashScreen(0x00ffff, 500);
					}
				}
			}
		});
		return true;
	};
	// Move a packet toward a node
	self.movePacketToNode = function (packet, node) {
		if (!packet || packet.isBeingDragged) return false;
		// Release current target if we have one
		if (self.target && self.target.isBeingDragged) {
			self.target.isBeingDragged = false;
		}
		// Set target packet as being dragged by bot
		self.target = packet;
		packet.isBeingDragged = true;
		// Visual feedback - move semibot to packet first
		tween(self, {
			x: packet.x,
			y: packet.y - 50
		}, {
			duration: 200,
			easing: tween.easeOutQuad,
			onFinish: function onFinish() {
				// Flash semibot
				LK.effects.flashObject(self, 0x00ffff, 300);
			}
		});
		// Move packet toward node with tweening for smooth movement
		tween(packet, {
			x: node.x,
			y: node.y - 20
		}, {
			duration: 500,
			easing: tween.easeOutQuad,
			onFinish: function onFinish() {
				// Release packet when it reaches the node
				packet.isBeingDragged = false;
				self.target = null;
				// Check for match at destination
				for (var i = 0; i < storageNodes.length; i++) {
					if (checkNodeMatch(packet, storageNodes[i])) {
						break;
					}
				}
			}
		});
		return true;
	};
	// Update method
	self.update = function () {
		if (!self.active || !gameRunning) return;
		// Track position for collision detection
		self.lastX = self.x;
		self.lastY = self.y;
		// Pulse effect
		var pulseAmount = Math.sin(LK.ticks * 0.05) * 0.05 + 1;
		self.scale.set(pulseAmount * 1.2, pulseAmount * 1.2);
		// Only act periodically to allow player to also interact
		if (LK.ticks - self.lastActionTime < self.actionDelay) return;
		// Skip action randomly to make bot feel more human
		if (Math.random() > 0.8) return;
		// PRIORITY 1: Attack villain if player is visible and villain exists
		if (player && villain && villain.active) {
			// If player is visible and villain exists, attack villain
			if (self.chaseVillain()) {
				self.lastActionTime = LK.ticks;
				// Visual feedback for semibot helping player
				LK.effects.flashObject(self, 0x00ffff, 300);
				return;
			}
		}
		// Check if we need to handle power-ups when stability is low
		if (stabilityBar.stability < 30 && powerUps.length > 0) {
			for (var i = 0; i < powerUps.length; i++) {
				var powerUp = powerUps[i];
				if (!powerUp.isBeingDragged && powerUp.y > 300) {
					// Move power-up to center to activate it
					tween(powerUp, {
						x: 2048 / 2,
						y: 2732 / 2
					}, {
						duration: 300,
						easing: tween.easeOutQuad,
						onFinish: function onFinish() {
							activatePowerUp(powerUp);
							var index = powerUps.indexOf(powerUp);
							if (index !== -1) {
								powerUps.splice(index, 1);
							}
							powerUp.destroy();
						}
					});
					self.lastActionTime = LK.ticks;
					return;
				}
			}
		}
		// Find and execute optimal move
		var move = self.findOptimalMove();
		if (move) {
			if (move.attackVillain) {
				// Chase and attack the villain
				self.chaseVillain();
			} else {
				// Regular packet matching behavior
				self.movePacketToNode(move.packet, move.node);
			}
			self.lastActionTime = LK.ticks;
		}
	};
	return self;
});
// Down event for dragging
var StabilityBar = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.stability = 50; // Start at 50%
	self.maxStability = 100;
	self.warningLevel = 25;
	self.criticalLevel = 10;
	// Create background
	var barBg = self.attachAsset('stabilityBarBg', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Create bar
	var bar = self.attachAsset('stabilityBar', {
		anchorX: 0.5,
		anchorY: 1.0
	});
	// Position the bar
	bar.y = barBg.height / 2;
	// Set stability amount (0-100)
	self.setStability = function (amount) {
		self.stability = Math.max(0, Math.min(self.maxStability, amount));
		// Update bar height
		var heightPercentage = self.stability / self.maxStability;
		bar.scaleY = heightPercentage;
		// Update bar color based on stability
		if (self.stability <= self.criticalLevel) {
			bar.tint = 0xe74c3c; // Critical - red
		} else if (self.stability <= self.warningLevel) {
			bar.tint = 0xf39c12; // Warning - orange
		} else {
			bar.tint = 0x27ae60; // Normal - green
		}
	};
	// Change stability
	self.changeStability = function (delta) {
		self.setStability(self.stability + delta);
		return self.stability;
	};
	// Initialize
	self.setStability(self.stability);
	return self;
});
// Game variables
var StorageNode = Container.expand(function () {
	var self = Container.call(this);
	// Color constants
	var COLORS = [0x3498db, 0xe74c3c, 0xf39c12, 0x2ecc71, 0x9b59b6];
	var COLOR_NAMES = ['blue', 'red', 'orange', 'green', 'purple'];
	// Properties
	self.colorIndex = 0;
	self.colorName = '';
	self.lastPacketsStored = 0;
	self.packetsStored = 0;
	// Create node graphic
	var nodeGraphic = self.attachAsset('storageNode', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	// Set color
	self.setColor = function (index) {
		self.colorIndex = index;
		self.colorName = COLOR_NAMES[index];
		nodeGraphic.tint = COLORS[index];
	};
	// Handle packet storage
	self.storePacket = function () {
		self.packetsStored++;
		// Visual feedback
		tween(self, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 200,
			onFinish: function onFinish() {
				tween(self, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200
				});
			}
		});
	};
	// Update method
	self.update = function () {
		self.lastPacketsStored = self.packetsStored;
	};
	return self;
});
var TaxmanPlayer = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.active = false;
	self.taxRate = 0.10; // 10% tax rate
	self.taxCollected = 0;
	self.collectionInterval = 120; // Collect every 2 seconds (120 frames)
	self.lastCollectionTime = 0;
	self.targetPacket = null;
	self.speed = 3; // Movement speed when chasing player
	self.lastX = 0;
	self.lastY = 0;
	self.chasingPlayer = false;
	self.playerChaseCooldown = 0;
	// Visual representation
	var taxmanGraphic = self.attachAsset('taxman', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	taxmanGraphic.tint = 0x990000; // Dark red color for taxman
	taxmanGraphic.scaleX = 1.2;
	taxmanGraphic.scaleY = 1.2;
	// Toggle taxman activity
	self.toggle = function () {
		self.active = !self.active;
		return self.active;
	};
	// Find a packet to tax
	self.findPacketToTax = function () {
		if (!packets.length) return null;
		// Find the highest value packet (prefer packets being matched)
		var bestPacket = null;
		var highestValue = -1;
		for (var i = 0; i < packets.length; i++) {
			var packet = packets[i];
			// Skip packets being dragged
			if (packet.isBeingDragged) continue;
			var value = 10; // Base value
			// Prefer packets that are about to be matched
			for (var j = 0; j < storageNodes.length; j++) {
				var node = storageNodes[j];
				if (packet.colorIndex === node.colorIndex) {
					// Distance to node (closer = higher value)
					var distance = Math.sqrt(Math.pow(packet.x - node.x, 2) + Math.pow(packet.y - node.y, 2));
					if (distance < 300) {
						value += 300 - distance;
					}
				}
			}
			if (value > highestValue) {
				highestValue = value;
				bestPacket = packet;
			}
		}
		return bestPacket;
	};
	// Apply tax to a packet (reduces its value)
	self.taxPacket = function (packet) {
		if (!packet) return;
		// Visual feedback
		self.x = packet.x;
		self.y = packet.y;
		// Flash effect
		tween(self, {
			scaleX: 1.5,
			scaleY: 1.5,
			alpha: 0.7
		}, {
			duration: 300,
			onFinish: function onFinish() {
				tween(self, {
					scaleX: 1.2,
					scaleY: 1.2,
					alpha: 1
				}, {
					duration: 300
				});
			}
		});
		// Collect tax
		self.taxCollected += 1;
		// Reduce packet value visually (make it slightly transparent)
		packet.alpha = 0.8;
		// Update UI
		if (taxmanStatusText) {
			taxmanStatusText.setText('Taxman: ' + (self.active ? 'Active' : 'Paused') + ' ($' + self.taxCollected + ')');
		}
	};
	// Audit the player (tax the player directly)
	self.auditPlayer = function () {
		if (!player) return;
		// Visual feedback
		tween(self, {
			scaleX: 1.7,
			scaleY: 1.7,
			alpha: 0.8
		}, {
			duration: 400,
			onFinish: function onFinish() {
				tween(self, {
					scaleX: 1.2,
					scaleY: 1.2,
					alpha: 1
				}, {
					duration: 300
				});
			}
		});
		// Take some health from player as "tax"
		player.takeDamage(5);
		// Increase tax collected
		self.taxCollected += 5;
		// Reset chase cooldown
		self.playerChaseCooldown = 300; // 5 seconds cooldown
		self.chasingPlayer = false;
		// Update UI
		if (taxmanStatusText) {
			taxmanStatusText.setText('Taxman: ' + (self.active ? 'Active' : 'Paused') + ' ($' + self.taxCollected + ')');
		}
	};
	// Chase the player
	self.chasePlayer = function () {
		if (!player) return;
		// Calculate direction to player
		var dx = player.x - self.x;
		var dy = player.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// If close enough to player, audit them
		if (distance < 80) {
			self.auditPlayer();
			return;
		}
		// Normalize direction and move towards player
		if (distance > 0) {
			dx = dx / distance;
			dy = dy / distance;
		}
		// Save last position
		self.lastX = self.x;
		self.lastY = self.y;
		// Move towards player
		self.x += dx * self.speed;
		self.y += dy * self.speed;
		// Keep within game bounds
		self.x = Math.max(50, Math.min(2048 - 50, self.x));
		self.y = Math.max(150, Math.min(2732 - 150, self.y));
	};
	// Update method
	self.update = function () {
		if (!self.active || !gameRunning) return;
		// Track last position
		self.lastX = self.x;
		self.lastY = self.y;
		// Decrease player chase cooldown if active
		if (self.playerChaseCooldown > 0) {
			self.playerChaseCooldown--;
		}
		// Decide whether to chase player or packets
		if (!self.chasingPlayer && self.playerChaseCooldown <= 0 && player && Math.random() < 0.01) {
			// 1% chance per frame to start chasing player when not on cooldown
			self.chasingPlayer = true;
		}
		// Chase player if in that mode
		if (self.chasingPlayer && player) {
			self.chasePlayer();
			return;
		}
		// Otherwise collect tax from packets
		if (LK.ticks - self.lastCollectionTime < self.collectionInterval) return;
		// Find and tax a packet
		var packetToTax = self.findPacketToTax();
		if (packetToTax) {
			self.taxPacket(packetToTax);
			self.lastCollectionTime = LK.ticks;
		}
	};
	return self;
});
var VillainPlayer = Container.expand(function () {
	var self = Container.call(this);
	// Villain properties
	self.active = true;
	self.health = 100;
	self.maxHealth = 100;
	self.attackCooldown = 180; // 3 seconds at 60fps
	self.currentCooldown = 0;
	self.target = null;
	self.targetingPlayer = true; // Alternates between targeting player and nodes
	self.attackDamage = 15;
	self.speed = 2;
	self.lastX = 0;
	self.lastY = 0;
	// Create villain graphic
	var villainGraphic = self.attachAsset('dataPacket', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	villainGraphic.tint = 0x800080; // Purple color for villain
	villainGraphic.scaleX = 1.4;
	villainGraphic.scaleY = 1.4;
	// Health bar background
	var healthBarBg = self.attachAsset('stabilityBarBg', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 1.2,
		scaleY: 0.2
	});
	healthBarBg.y = -50;
	// Health bar
	var healthBar = self.attachAsset('stabilityBar', {
		anchorX: 0,
		anchorY: 0.5,
		scaleX: 1.2,
		scaleY: 0.15
	});
	healthBar.y = -50;
	healthBar.x = -healthBarBg.width / 2;
	// Set health level
	self.setHealth = function (amount) {
		self.health = Math.max(0, Math.min(self.maxHealth, amount));
		var healthPercentage = self.health / self.maxHealth;
		healthBar.scaleX = 1.2 * healthPercentage;
		// Update bar color based on health
		if (self.health <= 20) {
			healthBar.tint = 0xe74c3c; // Critical - red
		} else if (self.health <= 50) {
			healthBar.tint = 0xf39c12; // Warning - orange
		} else {
			healthBar.tint = 0x800080; // Normal - purple
		}
	};
	// Take damage
	self.takeDamage = function (amount) {
		self.setHealth(self.health - amount);
		// Visual feedback
		LK.effects.flashObject(self, 0xff0000, 300);
		return self.health;
	};
	// Find target (player or storage node)
	self.findTarget = function () {
		// Target player if that's our current priority
		if (self.targetingPlayer && player) {
			self.target = player;
			return;
		}
		// Otherwise target a random storage node
		if (storageNodes.length > 0) {
			self.target = storageNodes[Math.floor(Math.random() * storageNodes.length)];
		}
	};
	// Attack target
	self.attackTarget = function () {
		self.currentCooldown = self.attackCooldown;
		// If targeting player, reduce player health
		if (self.target === player) {
			player.takeDamage(self.attackDamage);
			// Visual effect for attack
			tween(self, {
				scaleX: 1.8,
				scaleY: 1.8
			}, {
				duration: 200,
				onFinish: function onFinish() {
					tween(self, {
						scaleX: 1.4,
						scaleY: 1.4
					}, {
						duration: 200
					});
				}
			});
		}
		// If targeting node, create error blocks around it
		else if (storageNodes.indexOf(self.target) !== -1) {
			// Create error blocks around the node
			for (var i = 0; i < 3; i++) {
				var angle = Math.random() * Math.PI * 2;
				var distance = 80 + Math.random() * 40;
				var x = self.target.x + Math.cos(angle) * distance;
				var y = self.target.y + Math.sin(angle) * distance;
				createErrorBlock(x, y);
			}
			// Reduce stability
			stabilityBar.changeStability(-10);
			// Visual effect for attack
			tween(self, {
				rotation: Math.PI * 2
			}, {
				duration: 500,
				onFinish: function onFinish() {
					self.rotation = 0;
				}
			});
		}
		// Switch targeting priority
		self.targetingPlayer = !self.targetingPlayer;
	};
	// Move toward target
	self.moveTowardTarget = function () {
		if (!self.target) return;
		var dx = self.target.x - self.x;
		var dy = self.target.y - self.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// If close enough, attack
		if (distance < 100 && self.currentCooldown <= 0) {
			self.attackTarget();
			return;
		}
		// Normalize movement vector
		if (distance > 0) {
			dx = dx / distance;
			dy = dy / distance;
		}
		// Move villain toward target
		self.lastX = self.x;
		self.lastY = self.y;
		self.x += dx * self.speed;
		self.y += dy * self.speed;
		// Keep villain within game bounds
		self.x = Math.max(100, Math.min(2048 - 100, self.x));
		self.y = Math.max(200, Math.min(2732 - 200, self.y));
	};
	// Update method
	self.update = function () {
		if (!self.active || !gameRunning) return;
		// Decrease attack cooldown
		if (self.currentCooldown > 0) {
			self.currentCooldown--;
		}
		// Find new target if we don't have one
		if (!self.target || self.target === player && !player) {
			self.findTarget();
		}
		// Move toward target
		self.moveTowardTarget();
		// Pulse effect when attacking
		if (self.currentCooldown > self.attackCooldown - 30) {
			var pulseAmount = 1 + 0.2 * Math.sin(LK.ticks * 0.2);
			villainGraphic.scale.set(pulseAmount * 1.4, pulseAmount * 1.4);
		} else {
			villainGraphic.scale.set(1.4, 1.4);
		}
		// Track position for collision detection
		self.lastX = self.x;
		self.lastY = self.y;
	};
	// Initialize health
	self.setHealth(self.health);
	return self;
});
var Weapon = Container.expand(function () {
	var self = Container.call(this);
	// Properties
	self.damage = 10;
	self.cooldown = 30; // frames between shots
	self.currentCooldown = 0;
	self.range = 500;
	self.lastX = 0;
	self.lastY = 0;
	// Create weapon graphic
	var weaponGraphic = self.attachAsset('dataPacket', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.8,
		scaleY: 0.8
	});
	weaponGraphic.tint = 0x00ffff; // Cyan color for weapon
	// Update method
	self.update = function () {
		// Update cooldown
		if (self.currentCooldown > 0) {
			self.currentCooldown--;
		}
		// Track position for collision detection
		self.lastX = self.x;
		self.lastY = self.y;
	};
	// Fire method
	self.fire = function (targetX, targetY) {
		if (self.currentCooldown > 0) return null;
		// Reset cooldown
		self.currentCooldown = self.cooldown;
		// Create bullet
		var bullet = new Bullet();
		bullet.x = self.x;
		bullet.y = self.y;
		// Calculate direction to target
		var dx = targetX - self.x;
		var dy = targetY - self.y;
		var dist = Math.sqrt(dx * dx + dy * dy);
		// Normalize direction
		if (dist > 0) {
			dx = dx / dist;
			dy = dy / dist;
		}
		// Set bullet properties
		bullet.speedX = dx * 10;
		bullet.speedY = dy * 10;
		bullet.damage = self.damage;
		bullet.range = self.range;
		// Visual feedback
		tween(self, {
			scaleX: 1.2,
			scaleY: 1.2
		}, {
			duration: 100,
			onFinish: function onFinish() {
				tween(self, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 100
				});
			}
		});
		return bullet;
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000
});
/**** 
* Game Code
****/ 
// Tween library for animations
// Game assets
// Game variables
var packetSpawnInterval = 3 * 60; // Spawn every 3 seconds at 60fps
var nextPacketSpawn = packetSpawnInterval;
var stabilityDecayRate = 0.05; // Stability decay per tick
var packets = [];
var storageNodes = [];
var errorBlocks = [];
var powerUps = [];
var level = 1;
var score = 0;
var player;
var playerHealthText;
var stabilityBar;
var scoreText;
var levelText;
var botStatusText;
var draggedObject = null;
var gameRunning = true;
var lastSuccessfulChain = 0;
var chainCount = 0;
var semibot; // AI assistant player
var taxman; // Taxman player
var villain; // Villain player
var playerWeapon; // Player's weapon
var bullets = []; // Bullets in the game
var targetX = 0; // Target X for weapon
var targetY = 0; // Target Y for weapon
// UI text setup
scoreText = new Text2('Score: 0', {
	size: 60,
	fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 50;
scoreText.y = 50;
LK.gui.topLeft.addChild(scoreText);
// Add message to inform player they can only shoot villains
var shootInfoText = new Text2('You can only shoot villains!', {
	size: 50,
	fill: 0x00FFFF
});
shootInfoText.anchor.set(0.5, 0);
shootInfoText.x = 0;
shootInfoText.y = 540;
LK.gui.top.addChild(shootInfoText);
levelText = new Text2('Level: 1', {
	size: 60,
	fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
levelText.x = -50;
levelText.y = 50;
LK.gui.topRight.addChild(levelText);
// Bot status text
botStatusText = new Text2('Bot: Active', {
	size: 60,
	fill: 0x00FF00
});
botStatusText.anchor.set(0.5, 0);
botStatusText.x = 0;
botStatusText.y = 50;
LK.gui.top.addChild(botStatusText);
// Taxman status text
var taxmanStatusText = new Text2('Taxman: Paused ($0)', {
	size: 60,
	fill: 0xFF0000
});
taxmanStatusText.anchor.set(0.5, 0);
taxmanStatusText.x = 0;
taxmanStatusText.y = 230;
LK.gui.top.addChild(taxmanStatusText);
// Create toggle button
var toggleBotBtn = new Text2('Toggle Bot', {
	size: 60,
	fill: 0xFFFFFF
});
toggleBotBtn.anchor.set(0.5, 0);
toggleBotBtn.x = 0;
toggleBotBtn.y = 120;
toggleBotBtn.interactive = true;
toggleBotBtn.hitArea = new Rectangle(-150, 0, 300, 60);
// Toggle bot event
toggleBotBtn.down = function () {
	if (semibot) {
		var isActive = semibot.toggle();
		botStatusText.setText('Bot: ' + (isActive ? 'Active' : 'Paused'));
		botStatusText.style.fill = isActive ? 0x00FF00 : 0xFF0000;
	}
};
LK.gui.top.addChild(toggleBotBtn);
// Create toggle taxman button
var toggleTaxmanBtn = new Text2('Toggle Taxman', {
	size: 60,
	fill: 0xFFFFFF
});
toggleTaxmanBtn.anchor.set(0.5, 0);
toggleTaxmanBtn.x = 0;
toggleTaxmanBtn.y = 300;
toggleTaxmanBtn.interactive = true;
toggleTaxmanBtn.hitArea = new Rectangle(-150, 0, 300, 60);
// Toggle taxman event
toggleTaxmanBtn.down = function () {
	if (taxman) {
		var isActive = taxman.toggle();
		taxmanStatusText.setText('Taxman: ' + (isActive ? 'Active' : 'Paused') + ' ($' + taxman.taxCollected + ')');
		taxmanStatusText.style.fill = isActive ? 0x00FF00 : 0xFF0000;
	}
};
LK.gui.top.addChild(toggleTaxmanBtn);
// Player health text
playerHealthText = new Text2('Health: 100%', {
	size: 60,
	fill: 0x00FF00
});
playerHealthText.anchor.set(0, 1);
playerHealthText.x = 50;
playerHealthText.y = -50;
LK.gui.bottomLeft.addChild(playerHealthText);
// Initialize stability bar
stabilityBar = new StabilityBar();
stabilityBar.x = 80;
stabilityBar.y = 2732 / 2;
game.addChild(stabilityBar);
// Create storage nodes
function createStorageNodes() {
	// Clear existing nodes
	storageNodes.forEach(function (node) {
		node.destroy();
	});
	storageNodes = [];
	var numNodes = Math.min(5, 2 + Math.floor(level / 2)); // Increase nodes with level
	var spacing = 2048 / (numNodes + 1);
	for (var i = 0; i < numNodes; i++) {
		var node = new StorageNode();
		node.setColor(i % 5);
		node.x = spacing * (i + 1);
		node.y = 2732 - 300;
		game.addChild(node);
		storageNodes.push(node);
	}
}
// Create a new data packet
function createDataPacket() {
	var packet = new DataPacket();
	packet.x = Math.random() * (2048 - 200) + 100;
	packet.y = -100;
	game.addChild(packet);
	packets.push(packet);
	// Random vertical speed based on level
	packet.speedY = 1 + Math.random() * level * 0.5;
	return packet;
}
// Create power-up
function createPowerUp() {
	var powerUp = new PowerUp();
	powerUp.x = Math.random() * (2048 - 200) + 100;
	powerUp.y = -100;
	game.addChild(powerUp);
	powerUps.push(powerUp);
	// Set vertical speed
	powerUp.speedY = 1 + Math.random() * 0.5;
	return powerUp;
}
// Create error block obstacle
function createErrorBlock(x, y) {
	var block = new ErrorBlock();
	block.x = x;
	block.y = y;
	game.addChild(block);
	errorBlocks.push(block);
	return block;
}
// Handle power-up effect
function activatePowerUp(powerUp) {
	LK.getSound('powerup').play();
	switch (powerUp.type) {
		case 'slowTime':
			// Slow down all packets
			packets.forEach(function (packet) {
				packet.speedY *= 0.5;
			});
			break;
		case 'clearPath':
			// Remove all error blocks
			errorBlocks.forEach(function (block) {
				block.destroy();
			});
			errorBlocks = [];
			break;
		case 'stabilityBoost':
			// Boost stability
			stabilityBar.changeStability(25);
			break;
	}
	// Visual feedback
	LK.effects.flashScreen(0xf39c12, 500);
}
// Check if a packet matches a storage node
function checkNodeMatch(packet, node) {
	if (packet.colorIndex === node.colorIndex && packet.intersects(node)) {
		// Match found
		LK.getSound('match').play();
		// Update score based on chain
		if (lastSuccessfulChain === packet.colorIndex) {
			chainCount++;
			score += 10 * chainCount;
		} else {
			chainCount = 1;
			score += 10;
		}
		// Apply tax reduction if packet was taxed (indicated by alpha < 1)
		if (packet.alpha < 1 && taxman) {
			// Reduce score by taxman's rate
			var taxAmount = Math.floor(score * taxman.taxRate);
			score -= taxAmount;
			taxman.taxCollected += taxAmount;
			// Update taxman display
			if (taxmanStatusText) {
				taxmanStatusText.setText('Taxman: ' + (taxman.active ? 'Active' : 'Paused') + ' ($' + taxman.taxCollected + ')');
			}
		}
		lastSuccessfulChain = packet.colorIndex;
		// Update score display
		LK.setScore(score);
		scoreText.setText('Score: ' + score);
		// Increase stability
		stabilityBar.changeStability(5);
		// Visual feedback
		node.storePacket();
		// Remove packet
		var index = packets.indexOf(packet);
		if (index !== -1) {
			packets.splice(index, 1);
		}
		packet.destroy();
		// Level up check
		if (score >= level * 100) {
			levelUp();
		}
		return true;
	}
	return false;
}
// Handle level up
function levelUp() {
	level++;
	levelText.setText('Level: ' + level);
	// Increase difficulty
	packetSpawnInterval = Math.max(60, packetSpawnInterval - 15);
	stabilityDecayRate += 0.01;
	// Create new node layout
	createStorageNodes();
	// Visual feedback
	LK.effects.flashScreen(0x2ecc71, 500);
}
// Initialize semibot player
if (!semibot) {
	semibot = new SemibotPlayer();
	semibot.x = 2048 / 4; // Position at 1/4 of screen width
	semibot.y = 400; // Position near top
}
game.addChild(semibot);
// The semibot is now persistent across game resets
// Initialize taxman player
taxman = new TaxmanPlayer();
taxman.x = 100;
taxman.y = 300;
game.addChild(taxman);
// Initialize game
createStorageNodes();
// Initialize player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Initialize player weapon
playerWeapon = new Weapon();
playerWeapon.x = player.x;
playerWeapon.y = player.y - 30;
game.addChild(playerWeapon);
// Initialize villain
villain = new VillainPlayer();
villain.x = 2048 / 2;
villain.y = 300;
game.addChild(villain);
// Villain status text
var villainStatusText = new Text2('Villain: Active', {
	size: 60,
	fill: 0x800080
});
villainStatusText.anchor.set(0.5, 0);
villainStatusText.x = 0;
villainStatusText.y = 400;
LK.gui.top.addChild(villainStatusText);
// Create toggle villain button
var toggleVillainBtn = new Text2('Toggle Villain', {
	size: 60,
	fill: 0xFFFFFF
});
toggleVillainBtn.anchor.set(0.5, 0);
toggleVillainBtn.x = 0;
toggleVillainBtn.y = 470;
toggleVillainBtn.interactive = true;
toggleVillainBtn.hitArea = new Rectangle(-150, 0, 300, 60);
// Toggle villain event
toggleVillainBtn.down = function () {
	if (villain) {
		villain.active = !villain.active;
		villainStatusText.setText('Villain: ' + (villain.active ? 'Active' : 'Paused'));
		villainStatusText.style.fill = villain.active ? 0x800080 : 0xFF0000;
	}
};
LK.gui.top.addChild(toggleVillainBtn);
// Game touch/mouse events
game.down = function (x, y, obj) {
	// Check if we're clicking on a packet or power-up
	var foundObject = false;
	// Check packets first (in reverse to get top-most)
	for (var i = packets.length - 1; i >= 0; i--) {
		var packet = packets[i];
		if (packet.getBounds().contains(x, y)) {
			draggedObject = packet;
			packet.down(x, y, obj);
			foundObject = true;
			break;
		}
	}
	// Check power-ups if no packet was found
	if (!foundObject) {
		for (var i = powerUps.length - 1; i >= 0; i--) {
			var powerUp = powerUps[i];
			if (powerUp.getBounds().contains(x, y)) {
				draggedObject = powerUp;
				powerUp.down(x, y, obj);
				foundObject = true;
				break;
			}
		}
	}
};
game.move = function (x, y, obj) {
	if (draggedObject && draggedObject.isBeingDragged) {
		draggedObject.x = x;
		draggedObject.y = y;
	}
	// If player exists and no object is being dragged, move player toward touch position
	if (player && !draggedObject) {
		var dx = x - player.x;
		var dy = y - player.y;
		var distance = Math.sqrt(dx * dx + dy * dy);
		// Only move if touch is not too close to player
		if (distance > 30) {
			// Normalize and move
			dx = dx / distance;
			dy = dy / distance;
			player.move(dx, dy);
		}
		// Set target for weapon aiming
		targetX = x;
		targetY = y;
		// Fire weapon if cooldown allows
		if (playerWeapon && playerWeapon.currentCooldown <= 0) {
			var bullet = playerWeapon.fire(targetX, targetY);
			if (bullet) {
				game.addChild(bullet);
				bullets.push(bullet);
			}
		}
	}
};
game.up = function (x, y, obj) {
	if (draggedObject) {
		// Check for matches with storage nodes
		if (draggedObject instanceof DataPacket) {
			var matched = false;
			for (var i = 0; i < storageNodes.length; i++) {
				if (checkNodeMatch(draggedObject, storageNodes[i])) {
					matched = true;
					break;
				}
			}
			// Create error block if no match
			if (!matched) {
				LK.getSound('error').play();
				createErrorBlock(draggedObject.x, draggedObject.y);
				stabilityBar.changeStability(-10);
				// Remove the mismatched packet
				var index = packets.indexOf(draggedObject);
				if (index !== -1) {
					packets.splice(index, 1);
				}
				draggedObject.destroy();
			}
		}
		// Check if it's a power-up
		else if (draggedObject instanceof PowerUp) {
			activatePowerUp(draggedObject);
			// Remove the used power-up
			var index = powerUps.indexOf(draggedObject);
			if (index !== -1) {
				powerUps.splice(index, 1);
			}
			draggedObject.destroy();
		}
		draggedObject = null;
	}
};
// Main game update loop
game.update = function () {
	if (!gameRunning) return;
	// Update player
	if (player) {
		player.update();
		// Update player health text
		playerHealthText.setText('Health: ' + player.health + '%');
		// Ensure style object exists before setting fill property
		if (!playerHealthText.style) playerHealthText.style = {};
		playerHealthText.style.fill = player.health <= 20 ? 0xe74c3c : player.health <= 50 ? 0xf39c12 : 0x00FF00;
		// Check collisions with packets
		for (var i = packets.length - 1; i >= 0; i--) {
			if (player.intersects(packets[i]) && !packets[i].isBeingDragged) {
				if (packets[i].colorIndex === 0) {
					// Blue packets are good for player
					player.collectPacket(packets[i]);
					packets.splice(i, 1);
				}
			}
		}
		// Check collisions with error blocks
		for (var i = errorBlocks.length - 1; i >= 0; i--) {
			if (player.intersects(errorBlocks[i])) {
				player.takeDamage(0.2); // Slow damage over time
			}
		}
		// Game over if player dies
		if (player.health <= 0 && gameRunning) {
			gameRunning = false;
			LK.effects.flashScreen(0xe74c3c, 1000);
			LK.showGameOver();
		}
	}
	// Update player weapon
	if (playerWeapon) {
		playerWeapon.update();
	}
	// Update semibot if active
	if (semibot) {
		semibot.update();
	}
	// Update taxman if active
	if (taxman) {
		taxman.update();
		// Check for player-taxman collisions when not currently auditing
		if (player && player.intersects(taxman) && taxman.active && taxman.playerChaseCooldown <= 0) {
			taxman.auditPlayer();
			// Add knockback effect when colliding
			var dx = player.x - taxman.x;
			var dy = player.y - taxman.y;
			var dist = Math.sqrt(dx * dx + dy * dy);
			if (dist > 0) {
				dx = dx / dist;
				dy = dy / dist;
				tween(player, {
					x: player.x + dx * 40,
					y: player.y + dy * 40
				}, {
					duration: 200
				});
			}
		}
	}
	// Update villain if active
	if (villain) {
		villain.update();
		// Check for player-villain collisions
		if (player && player.intersects(villain) && villain.currentCooldown <= 0) {
			// Damage player on collision
			player.takeDamage(5);
			villain.currentCooldown = 60; // Set a cooldown
			// Knockback effect
			var dx = player.x - villain.x;
			var dy = player.y - villain.y;
			var dist = Math.sqrt(dx * dx + dy * dy);
			if (dist > 0) {
				dx = dx / dist;
				dy = dy / dist;
				// Apply knockback to both
				tween(player, {
					x: player.x + dx * 50,
					y: player.y + dy * 50
				}, {
					duration: 200
				});
				tween(villain, {
					x: villain.x - dx * 30,
					y: villain.y - dy * 30
				}, {
					duration: 200
				});
			}
		}
		// Check for packet-villain collisions
		for (var i = packets.length - 1; i >= 0; i--) {
			if (packets[i].intersects(villain)) {
				// Villain steals packets
				packets[i].destroy();
				packets.splice(i, 1);
				// Villain gains health
				villain.setHealth(villain.health + 5);
				// Visual feedback
				tween(villain, {
					scaleX: 1.6,
					scaleY: 1.6
				}, {
					duration: 200,
					onFinish: function onFinish() {
						tween(villain, {
							scaleX: 1.4,
							scaleY: 1.4
						}, {
							duration: 200
						});
					}
				});
			}
		}
	}
	// Update all game objects
	for (var i = packets.length - 1; i >= 0; i--) {
		var packet = packets[i];
		packet.update();
		// Move non-dragged packets down
		if (!packet.isBeingDragged) {
			packet.y += packet.speedY;
		}
		// Check if packet is out of bounds
		if (packet.y > 2732 + 100) {
			// Missed packet - penalty
			stabilityBar.changeStability(-15);
			LK.getSound('error').play();
			// Remove packet
			packets.splice(i, 1);
			packet.destroy();
			continue;
		}
	}
	// Update bullets
	for (var i = bullets.length - 1; i >= 0; i--) {
		var bullet = bullets[i];
		bullet.update();
		// Check if bullet is out of bounds or exceeded range
		if (bullet.x < -50 || bullet.x > 2048 + 50 || bullet.y < -50 || bullet.y > 2732 + 50 || bullet.distanceTraveled > bullet.range) {
			bullets.splice(i, 1);
			bullet.destroy();
			continue;
		}
		// Check bullet collisions with villain
		if (villain && bullet.intersects(villain)) {
			// Damage villain
			villain.takeDamage(bullet.damage);
			// Remove bullet
			bullets.splice(i, 1);
			bullet.destroy();
			// Increase score
			score += 5;
			LK.setScore(score);
			scoreText.setText('Score: ' + score);
			continue;
		}
		// We've removed the code that checked for bullet collisions with error blocks
		// as player can only shoot villains now
	}
	// Update power-ups
	for (var i = powerUps.length - 1; i >= 0; i--) {
		var powerUp = powerUps[i];
		powerUp.update();
		// Move non-dragged power-ups down
		if (!powerUp.isBeingDragged) {
			powerUp.y += powerUp.speedY;
		}
		// Remove if out of bounds
		if (powerUp.y > 2732 + 100) {
			powerUps.splice(i, 1);
			powerUp.destroy();
		}
	}
	// Update error blocks
	for (var i = errorBlocks.length - 1; i >= 0; i--) {
		var block = errorBlocks[i];
		block.update();
		// Remove expired blocks
		if (block.lifespan <= 0) {
			errorBlocks.splice(i, 1);
			block.destroy();
		}
	}
	// Update storage nodes
	for (var i = 0; i < storageNodes.length; i++) {
		storageNodes[i].update();
	}
	// Spawn new packets
	nextPacketSpawn--;
	if (nextPacketSpawn <= 0) {
		createDataPacket();
		nextPacketSpawn = packetSpawnInterval;
		// Occasionally spawn power-ups (5% chance)
		if (Math.random() < 0.05) {
			createPowerUp();
		}
	}
	// Decrease stability over time
	stabilityBar.changeStability(-stabilityDecayRate);
	// Warning when stability is low
	if (stabilityBar.stability <= stabilityBar.warningLevel && LK.ticks % 60 === 0) {
		LK.getSound('warning').play();
	}
	// Check for game over
	if (stabilityBar.stability <= 0) {
		gameRunning = false;
		LK.effects.flashScreen(0xe74c3c, 1000);
		LK.showGameOver();
	}
};
// Set background color to dark blue/grey for tech theme
game.setBackgroundColor(0x2c3e50); ===================================================================
--- original.js
+++ change.js
@@ -37,8 +37,10 @@
 		var dy = self.y - self.lastY;
 		self.distanceTraveled += Math.sqrt(dx * dx + dy * dy);
 		// Rotate in direction of movement
 		self.rotation = Math.atan2(self.speedY, self.speedX);
+		// Store if this bullet has already hit something
+		self.isDestroyed = false;
 	};
 	return self;
 });
 var DataPacket = Container.expand(function () {
@@ -1032,8 +1034,17 @@
 scoreText.anchor.set(0, 0);
 scoreText.x = 50;
 scoreText.y = 50;
 LK.gui.topLeft.addChild(scoreText);
+// Add message to inform player they can only shoot villains
+var shootInfoText = new Text2('You can only shoot villains!', {
+	size: 50,
+	fill: 0x00FFFF
+});
+shootInfoText.anchor.set(0.5, 0);
+shootInfoText.x = 0;
+shootInfoText.y = 540;
+LK.gui.top.addChild(shootInfoText);
 levelText = new Text2('Level: 1', {
 	size: 60,
 	fill: 0xFFFFFF
 });
@@ -1556,24 +1567,10 @@
 			LK.setScore(score);
 			scoreText.setText('Score: ' + score);
 			continue;
 		}
-		// Check bullet collisions with error blocks
-		for (var j = errorBlocks.length - 1; j >= 0; j--) {
-			if (bullet.intersects(errorBlocks[j])) {
-				// Remove error block
-				errorBlocks[j].destroy();
-				errorBlocks.splice(j, 1);
-				// Remove bullet
-				bullets.splice(i, 1);
-				bullet.destroy();
-				// Increase score slightly
-				score += 2;
-				LK.setScore(score);
-				scoreText.setText('Score: ' + score);
-				break;
-			}
-		}
+		// We've removed the code that checked for bullet collisions with error blocks
+		// as player can only shoot villains now
 	}
 	// Update power-ups
 	for (var i = powerUps.length - 1; i >= 0; i--) {
 		var powerUp = powerUps[i];
 Stroge node. In-Game asset. High contrast. No shadows
 Error. In-Game asset. High contrast. No shadows
 Power up. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
 Semibot. In-Game asset. High contrast. No shadows
 (StabilityBarBg). In-Game asset. High contrast. No shadows
 Creepy sad and happy emoji with black eyes and white people's. In-Game asset. High contrast. No shadows
 Blue semibot. In-Game asset. High contrast. No shadows