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);
/****
* 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);
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