User prompt
Размести по низу две картинки города с лева на право
User prompt
Нужно замастить низ картинкой города. А то слишком растянута
User prompt
Нужно замастить низ картинкой города. А то слишком растянута
User prompt
А теперь чуть ближе
User prompt
Дым должен создаваться чуть подальше от пушки
User prompt
Я не вижу пушки она исчезла или прозрачная
User prompt
Достаточно двух частей. Основпния и puha2 (ствол)
User prompt
Только дульная часть
User prompt
Please fix the bug: 'ReferenceError: puhaInstance is not defined' in or related to this line: 'var muzzleAngle = puhaInstance.rotation + (Math.random() - 0.5) * 0.5; // Small angle variation' Line Number: 1243
User prompt
Но отъезжать должна только часть пушки а другая стоять не подвижно ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
На то место где появляются частицы
User prompt
Еще дальше
User prompt
А теперь к правому
User prompt
Смести пуха2 к левому краю пушки
User prompt
Смести puha2 к коаю пушки
User prompt
Прикрепи к пушке puha 2
User prompt
Добавь Puha2 к пушке с правой стороны. Она ее продолжение
User prompt
Сделай пушку прозрачной
User prompt
Не вижу puha2 она должна быть продолжением пушки справой стороны
User prompt
Я разделил пуху на две части пуха2 это дуло которое должно отъезжать назад во время вычтрела. Приделай ее к пухе в нужном месте ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Частицы пушки должны быть крупнее. Это как дым ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Сдвинь пушку вправо на 400
User prompt
Картинка города всегда должна перекрывать пушку
User prompt
Опумти пушку ниже на 200
User prompt
Сдвинь частицы пушки вправо на 20
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var City = Container.expand(function () { var self = Container.call(this); self.takeDamage = function (damageAmount) { if (self.health <= 0) return; // Already destroyed if (self.shielded) return; // Shield active, ignore damage self.health -= damageAmount; LK.effects.flashObject(self.graphic, 0xFF0000, 300); // Flash city red if (self.healthDisplay) { self.healthDisplay.setText('City Health: ' + Math.max(0, self.health) + ' / ' + CITY_MAX_HEALTH); } if (self.health <= 0) { self.health = 0; // Cap at 0 LK.showGameOver(); // Trigger game over } }; self.setHealthDisplay = function (textObject) { self.healthDisplay = textObject; self.healthDisplay.setText('City Health: ' + self.health + ' / ' + CITY_MAX_HEALTH); }; // Initialization self.graphic = self.attachAsset('city', { anchorX: 0.5, anchorY: 1.0 }); // Anchor bottom-center self.health = CITY_MAX_HEALTH; self.healthDisplay = null; // To be linked to a Text2 object return self; }); var Crosshair = Container.expand(function () { var self = Container.call(this); self.isOver = function (target) { if (!target || !target.graphic || !self.graphic || !target.graphic.width || !target.graphic.height) return false; // Basic bounding box collision var selfHalfWidth = self.graphic.width / 2; var selfHalfHeight = self.graphic.height / 2; var targetHalfWidth = target.graphic.width * (target.graphic.scaleX || 1) / 2; var targetHalfHeight = target.graphic.height * (target.graphic.scaleY || 1) / 2; var selfLeft = self.x - selfHalfWidth; var selfRight = self.x + selfHalfWidth; var selfTop = self.y - selfHalfHeight; var selfBottom = self.y + selfHalfHeight; var targetLeft = target.x - targetHalfWidth; var targetRight = target.x + targetHalfWidth; var targetTop = target.y - targetHalfHeight; var targetBottom = target.y + targetHalfHeight; return !(selfRight < targetLeft || selfLeft > targetRight || selfBottom < targetTop || selfTop > targetBottom); }; // Initialization code, graphic will be attached in Game Code self.graphic = null; self.speed = 0; // Will be set from game constant return self; }); // Base class for falling objects var FallingObject = Container.expand(function (assetId, initialHealth, fallSpeed, objectType) { var self = Container.call(this); self.takeDamage = function (damageAmount) { if (self.isDestroyed) return; self.health -= damageAmount; LK.effects.flashObject(self.graphic, 0xFF0000, 100); // Red flash on hit // Create floating damage number var damageNumber = new Text2(damageAmount.toString(), { size: 60, fill: 0xFFFFFF, align: 'center', font: 'Impact' }); damageNumber.anchor.set(0.5, 0.5); damageNumber.x = self.x + (Math.random() - 0.5) * 40; // Slight random offset damageNumber.y = self.y - 20; game.addChild(damageNumber); // Animate damage number floating up without fading out tween(damageNumber, { y: damageNumber.y - 120, scaleX: 1.2, scaleY: 1.2 }, { duration: 1200, easing: tween.easeOut }); // Remove damage number after timeout LK.setTimeout(function () { if (damageNumber.parent) { game.removeChild(damageNumber); } }, 1200); // Add pulsing scale animation when hit tween.stop(self.graphic, { scaleX: true, scaleY: true }); // Stop any existing scale tweens tween(self.graphic, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self.graphic, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeIn }); } }); if (self.health <= 0) { self.destroySelf(); } }; self.destroySelf = function () { if (self.isDestroyed) return; self.isDestroyed = true; // Create kusok fragments explosion var fragmentCount = 3 + Math.floor(Math.random() * 4); // Random between 3 and 6 kusok fragments for (var p = 0; p < fragmentCount; p++) { var randomScale = 0.4 + Math.random() * 0.8; // Random scale between 0.4 and 1.2 var kusokFragment = LK.getAsset('kusok', { anchorX: 0.5, anchorY: 0.5, scaleX: randomScale, scaleY: randomScale }); kusokFragment.x = self.x; kusokFragment.y = self.y; game.addChild(kusokFragment); // Random direction and speed for each fragment var angle = Math.random() * Math.PI * 2; var speed = 80 + Math.random() * 120; // Random speed between 80-200 var velocityX = Math.cos(angle) * speed; var velocityY = Math.sin(angle) * speed; // Random rotation speed and direction for each fragment (slower rotation) var rotationSpeed = 0.01 + Math.random() * 0.03; // Random rotation speed between 0.01 and 0.04 radians per frame var rotationDirection = Math.random() < 0.5 ? 1 : -1; // Random direction (clockwise or counterclockwise) var totalRotation = rotationSpeed * rotationDirection * 120; // Total rotation over 2 seconds (120 frames at 60fps) // Animate fragment movement, rotation, and scale down over 2 seconds tween(kusokFragment, { x: kusokFragment.x + velocityX, y: kusokFragment.y + velocityY, scaleX: 0, scaleY: 0, rotation: kusokFragment.rotation + totalRotation }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { if (kusokFragment.parent) { game.removeChild(kusokFragment); } } }); } // Add a small particle explosion or shrink effect if (tween) { tween.stop(self.graphic); // Stop any ongoing tweens tween(self.graphic, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 200, onFinish: function onFinish() { // Actual removal from game will be handled in game.update } }); } // Play boom sound when object is destroyed LK.getSound('Boom').play(); // Points for destroying object var moneyEarned = self.objectType === 'meteor' ? 10 : 15; LK.setScore(LK.getScore() + moneyEarned); updateScoreDisplay(); // Create floating money number display var moneyNumber = new Text2('$' + moneyEarned.toString(), { size: 110, fill: 0x00FF00, align: 'center', font: 'Impact' }); moneyNumber.anchor.set(0.5, 0.5); moneyNumber.x = self.x + (Math.random() - 0.5) * 50; // Slight random offset moneyNumber.y = self.y - 30; game.addChild(moneyNumber); // Animate money number floating up without fading out tween(moneyNumber, { y: moneyNumber.y - 150, scaleX: 1.4, scaleY: 1.4 }, { duration: 1500, easing: tween.easeOut }); // Remove money number after timeout LK.setTimeout(function () { if (moneyNumber.parent) { game.removeChild(moneyNumber); } }, 1500); }; self.update = function () { if (self.isDestroyed) return; self.y += self.speedY; }; //{-|u} // Initialization self.graphic = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.health = initialHealth; self.maxHealth = initialHealth; // Store max health for potential future use (e.g. health bars on objects) self.speedY = fallSpeed; self.isDestroyed = false; self.objectType = objectType; // 'meteor' or 'alien' return self; }); var MeteorFast = FallingObject.expand(function () { var self = FallingObject.call(this, 'meteorfast1', 60, METEOR_SPEED * 1.35, 'meteor'); // 60% health, 35% faster speed (reduced from 80% faster) // Add rotation animation to meteor self.rotationSpeed = 0.008 + Math.random() * 0.015; // Faster rotation than regular meteor self.rotationDirection = Math.random() < 0.5 ? 1 : -1; // Random direction (clockwise or counterclockwise) self.update = function () { if (self.isDestroyed) return; self.y += self.speedY; // Add rotation self.graphic.rotation += self.rotationSpeed * self.rotationDirection; }; return self; }); var Meteor = FallingObject.expand(function () { var self = FallingObject.call(this, 'meteor', METEOR_HEALTH, METEOR_SPEED / 2, 'meteor'); // Add rotation animation to meteor self.rotationSpeed = 0.005 + Math.random() * 0.01; // Random rotation speed between 0.005 and 0.015 radians per frame self.rotationDirection = Math.random() < 0.5 ? 1 : -1; // Random direction (clockwise or counterclockwise) self.update = function () { if (self.isDestroyed) return; self.y += self.speedY; // Add rotation self.graphic.rotation += self.rotationSpeed * self.rotationDirection; }; return self; }); var Alien = FallingObject.expand(function () { var self = FallingObject.call(this, 'alien', ALIEN_HEALTH, ALIEN_SPEED / 2, 'alien'); // Alien-specific properties or methods (e.g., different movement pattern) return self; }); var VirtualJoystick = Container.expand(function () { var self = Container.call(this); self.initialize = function (baseAssetId, knobAssetId, visualRadius, interactionRadius) { // baseGraphic and knobGraphic are children of 'self' (the joystick container) // Their positions (0,0) will be the center of 'self'. self.baseGraphic = self.addChild(LK.getAsset(baseAssetId, { anchorX: 0.5, anchorY: 0.5 })); self.knobGraphic = self.addChild(LK.getAsset(knobAssetId, { anchorX: 0.5, anchorY: 0.5 })); self.visualRadius = visualRadius; // Max distance knob can move visually self.interactionRadius = interactionRadius; // Touchable area around joystick center self.active = false; self.touchId = null; self.deltaX = 0; // Normalized movement vector x (-1 to 1) self.deltaY = 0; // Normalized movement vector y (-1 to 1) self.initialTouchX = 0; // Track initial touch position self.initialTouchY = 0; // Track initial touch position }; self.processDown = function (gameGlobalX, gameGlobalY, eventObj, treatTouchAsLocalOrigin) { var localClickPos; if (treatTouchAsLocalOrigin) { // If the joystick was just repositioned to gameGlobalX, gameGlobalY (the touch location), // then this touch point is effectively at the joystick's new local origin (0,0). localClickPos = { x: 0, y: 0 }; } else { // Standard case: joystick is already positioned. Convert global touch to local. localClickPos = self.toLocal({ x: gameGlobalX, y: gameGlobalY }); } var distSq = localClickPos.x * localClickPos.x + localClickPos.y * localClickPos.y; if (distSq <= self.interactionRadius * self.interactionRadius) { self.active = true; self.touchId = eventObj.identifier; // Use identifier from the event object // Always start knob at center when joystick is activated self.knobGraphic.x = 0; self.knobGraphic.y = 0; self.deltaX = 0; self.deltaY = 0; // Store the initial touch position to avoid coordinate transformation issues self.initialTouchX = gameGlobalX; self.initialTouchY = gameGlobalY; return true; // Consumed event } return false; }; self.processMove = function (gameGlobalX, gameGlobalY, eventObj) { if (!self.active || eventObj.identifier !== undefined && eventObj.identifier !== self.touchId) { return; } // Calculate movement relative to initial touch position var deltaX = gameGlobalX - self.initialTouchX; var deltaY = gameGlobalY - self.initialTouchY; self.updateKnobPosition(deltaX, deltaY); }; self.processUp = function (eventObj) { if (!self.active || eventObj.identifier !== undefined && eventObj.identifier !== self.touchId) { return; } self.active = false; self.touchId = null; self.knobGraphic.x = 0; // Reset to center of joystick container self.knobGraphic.y = 0; self.deltaX = 0; self.deltaY = 0; }; self.updateKnobPosition = function (localX, localY) { var dx = localX; var dy = localY; var distance = Math.sqrt(dx * dx + dy * dy); var clampedX = dx; var clampedY = dy; if (distance > self.visualRadius) { clampedX = dx / distance * self.visualRadius; clampedY = dy / distance * self.visualRadius; } self.knobGraphic.x = clampedX; self.knobGraphic.y = clampedY; if (self.visualRadius === 0) { // Avoid division by zero if radius is 0 self.deltaX = 0; self.deltaY = 0; } else { self.deltaX = clampedX / self.visualRadius; self.deltaY = clampedY / self.visualRadius; } self.deltaX = Math.max(-1, Math.min(1, self.deltaX)); self.deltaY = Math.max(-1, Math.min(1, self.deltaY)); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0a18 // Even darker blue for space }); /**** * Game Code ****/ // --- Game Constants --- // Brownish meteor // Lime green alien (can be simple box for now) // Grey city base // A simple red circle for the crosshair var WORLD_WIDTH = 2048; var WORLD_HEIGHT = 2732; var METEOR_HEALTH = 100; var METEOR_SPEED = 3; // Pixels per frame var METEOR_DAMAGE_TO_CITY = 75; var ALIEN_HEALTH = 150; var ALIEN_SPEED = 2.5; var ALIEN_DAMAGE_TO_CITY = 100; var CITY_MAX_HEALTH = 1000; // var CLICK_DAMAGE = 35; // Replaced by FIRE_DAMAGE // var HOLD_DAMAGE_PER_INTERVAL = 20; // Replaced by FIRE_DAMAGE // var HOLD_DAMAGE_INTERVAL_MS = 150; // Replaced by FIRE_RATE_MS var CROSSHAIR_SPEED = 10; var JOYSTICK_VISUAL_RADIUS = 180; // Increased max knob center travel distance for larger joystick var JOYSTICK_INTERACTION_RADIUS = 260; // Increased touchable area radius for larger joystick var FIRE_RATE_MS = 1000; // How often the player can fire (1 shot per second) var FIRE_DAMAGE = 35; // Damage per shot from crosshair var SPAWN_INTERVAL_MS_MIN = 2400; // Minimum time between spawns var SPAWN_INTERVAL_MS_MAX = 5000; // Maximum time between spawns // --- Global Game Variables --- var cityInstance; var fallingObjects = []; var cityHealthText; // For GUI var scoreText; // For player score // var playerHoldingTarget = null; // Removed // var isPlayerHolding = false; // Replaced by isFiring // var holdDamageInterval = null; // Removed var crosshairInstance; var joystickInstance; var isFiring = false; var lastFireTime = 0; var joystickTouchId = null; // To track the touch interacting with the joystick var nextSpawnTime = 0; var stars = []; var STAR_COUNT = 50; // --- Helper Functions --- function spawnFallingObject() { var randomValue = Math.random(); var newObject; if (randomValue < 0.4) { // 40% regular meteors newObject = new Meteor(); } else if (randomValue < 0.65) { // 25% fast meteors newObject = new MeteorFast(); } else { newObject = new Alien(); // 35% aliens } // Position just above the screen, at a random horizontal position newObject.x = Math.random() * (WORLD_WIDTH - newObject.graphic.width) + newObject.graphic.width / 2; newObject.y = -newObject.graphic.height / 2; // Start just off-screen top fallingObjects.push(newObject); game.addChild(newObject); } function updateScoreDisplay() { if (scoreText) { scoreText.setText('$' + LK.getScore()); } } function createStars() { for (var i = 0; i < STAR_COUNT; i++) { // Randomly choose star size var starTypes = ['star_small', 'star_medium', 'star_large']; var starWeights = [0.6, 0.3, 0.1]; // 60% small, 30% medium, 10% large var randomValue = Math.random(); var starType = 'star_small'; if (randomValue > 0.6 && randomValue <= 0.9) { starType = 'star_medium'; } else if (randomValue > 0.9) { starType = 'star_large'; } var star = LK.getAsset(starType, { anchorX: 0.5, anchorY: 0.5 }); star.x = Math.random() * WORLD_WIDTH; star.y = Math.random() * WORLD_HEIGHT; star.alpha = 0.3 + Math.random() * 0.7; // Random initial brightness star.twinkleDelay = Math.random() * 3000; // Random delay before first twinkle star.nextTwinkleTime = LK.ticks * (1000 / 60) + star.twinkleDelay; star.isTwinkling = false; // Track if star is currently twinkling star.lastColorChangeTime = LK.ticks * (1000 / 60); // Initialize last color change time stars.push(star); game.addChild(star); } } function updateStars() { var currentTime = LK.ticks * (1000 / 60); var colors = [0x4169E1, 0x00FFFF, 0xFFFFFF, 0xFFFF00]; // Blue, Cyan, White, Yellow for (var i = 0; i < stars.length; i++) { var star = stars[i]; // Check if star should change color (either due to regular twinkle time or 15-second limit) var shouldTwinkle = currentTime >= star.nextTwinkleTime; var timeSinceLastChange = currentTime - (star.lastColorChangeTime || 0); var forcedChange = timeSinceLastChange >= 15000; // 15 seconds if ((shouldTwinkle || forcedChange) && !star.isTwinkling) { // Start twinkling animation star.isTwinkling = true; // Pick a new color different from the current one var currentColor = star.tint; var availableColors = []; for (var c = 0; c < colors.length; c++) { if (colors[c] !== currentColor) { availableColors.push(colors[c]); } } // Fallback in case all colors are the same (shouldn't happen) if (availableColors.length === 0) { availableColors = colors.slice(); } var targetColor = availableColors[Math.floor(Math.random() * availableColors.length)]; var targetAlpha = 0.1 + Math.random() * 0.9; // Random target brightness // Animate both color and alpha tween(star, { alpha: targetAlpha, tint: targetColor }, { duration: 500 + Math.random() * 1500, easing: tween.easeInOut, onFinish: function (starRef, colorRef) { return function onFinish() { // Set next twinkle time (3-8 seconds from now, but no more than 15 seconds total) var now = LK.ticks * (1000 / 60); starRef.lastColorChangeTime = now; var nextInterval = Math.min(3000 + Math.random() * 5000, 15000); starRef.nextTwinkleTime = now + Math.max(nextInterval, 1000); // Ensure at least 1 second starRef.isTwinkling = false; // Set the color to the final value to avoid rounding issues starRef.tint = colorRef; }; }(star, targetColor) }); } } } // --- Game Initialization --- // Reset upgrades and turret for (var k in upgrades) upgrades[k] = 0; if (game.turretInterval) { LK.clearInterval(game.turretInterval); game.turretInterval = null; } // Add background var background = LK.getAsset('fon', { anchorX: 0, anchorY: 0 }); background.x = 0; background.y = 0; game.addChild(background); // Create twinkling stars createStars(); // Create and position the city cityInstance = new City(); cityInstance.x = WORLD_WIDTH / 2; cityInstance.y = WORLD_HEIGHT; // Bottom edge of city at bottom of screen game.addChild(cityInstance); // Setup City Health Display cityHealthText = new Text2('', { size: 60, fill: 0xFFFFFF, align: 'center', font: 'Impact' }); cityHealthText.anchor.set(0.5, 1); // Anchor bottom-center for text LK.gui.bottom.addChild(cityHealthText); // Add to bottom center of GUI cityInstance.setHealthDisplay(cityHealthText); // Link to city instance // Setup Score Display LK.setScore(0); // Initialize score scoreText = new Text2('$0', { size: 90, fill: 0x00FF00, align: 'center', font: 'Impact' }); scoreText.anchor.set(0.5, 0); // Anchor top-center // Position score text slightly below the top edge to avoid any engine icons, if necessary // LK.gui.top has y=0. We'll add a small offset. scoreText.y = 60; // Moved higher by 20 pixels (was 80, now 60). LK.gui.top.addChild(scoreText); // --- Event Handlers --- // --- Upgrades System --- // Upgrade state variables var upgrades = { fireDamage: 0, // Level fireRate: 0, // Level cityHeal: 0, // Level shield: 0, // Level turret: 0, // Level crosshairSize: 0 // Level }; var upgradesPopup = null; var upgradesButton = null; var upgradesButtonActive = false; var upgradesButtonWidth = 180; var upgradesButtonHeight = 90; var upgradesButtonPadding = 30; var upgradesButtonY = 20; // Offset from top var upgradesButtonX = WORLD_WIDTH - upgradesButtonWidth - upgradesButtonPadding; // Upgrade definitions var UPGRADE_DEFS = [{ key: 'fireDamage', name: 'Fire Damage +', desc: 'Increase shot damage', cost: function cost(level) { return 50 + 50 * level; }, max: 5 }, { key: 'fireRate', name: 'Fire Rate +', desc: 'Reduce delay between shots', cost: function cost(level) { return 60 + 60 * level; }, max: 5 }, { key: 'cityHeal', name: 'Heal City', desc: 'Restore city health', cost: function cost(level) { return 80 + 80 * level; }, max: 3 }, { key: 'shield', name: 'Shield', desc: 'Temporary city shield', cost: function cost(level) { return 120 + 80 * level; }, max: 1 }, { key: 'turret', name: 'Turret', desc: 'Automatic turret', cost: function cost(level) { return 200 + 100 * level; }, max: 1 }, { key: 'crosshairSize', name: 'Crosshair Size +', desc: 'Increase crosshair size', cost: function cost(level) { return 40 + 40 * level; }, max: 3 }]; // Create upgrades button (top right, not in topLeft) upgradesButton = LK.getAsset('upgrade', { anchorX: 1, anchorY: 0, scaleX: upgradesButtonWidth / 800, // Scale to fit button size (upgrade asset is 800px wide) scaleY: upgradesButtonHeight / 400 // Scale to fit button size (upgrade asset is 400px tall) }); // Place button at top right, but not in the top left 100x100 area upgradesButton.x = -upgradesButtonPadding; // Will be positioned by LK.gui.topRight upgradesButton.y = upgradesButtonY; upgradesButton.interactive = true; upgradesButton.buttonMode = true; upgradesButton.visible = true; upgradesButtonActive = false; LK.gui.topRight.addChild(upgradesButton); // Also add a large invisible hit area to make it easier to tap if (!upgradesButton.hitArea) { var hitArea = LK.getAsset('fon', { anchorX: 1, anchorY: 0, scaleX: 220 / 2048, scaleY: 100 / 2732 }); hitArea.x = upgradesButton.x; hitArea.y = upgradesButton.y; hitArea.alpha = 0.01; hitArea.interactive = true; hitArea.buttonMode = true; LK.gui.topRight.addChild(hitArea); hitArea.on('down', function (x, y, obj) { if (!upgradesButtonActive) showUpgradesPopup(); }); } // Upgrades popup container function showUpgradesPopup() { if (upgradesPopup) return; // Already open // Slow down all enemies by 90% for (var i = 0; i < fallingObjects.length; i++) { var obj = fallingObjects[i]; if (!obj.isDestroyed) { obj.originalSpeedY = obj.speedY; // Store original speed tween(obj, { speedY: obj.speedY * 0.1 }, { duration: 200 }); // Slow to 10% of original speed } } LK.getSound('menu').play(); upgradesPopup = new Container(); // Dimmed background for upgrades popup with its own color var popupBg = LK.getAsset('fon', { anchorX: 0, anchorY: 0, scaleX: 1, scaleY: 1 }); popupBg.tint = 0x1a1a2e; // Dark blue-purple color for upgrades background popupBg.alpha = 0.85; // Make it semi-transparent popupBg.width = WORLD_WIDTH; popupBg.height = WORLD_HEIGHT; popupBg.interactive = true; // Allow closing popup by tapping background popupBg.buttonMode = true; popupBg.on('down', function (x, y, obj) { closeUpgradesPopup(); }); upgradesPopup.addChild(popupBg); // Popup window var popupWidth = 1800; var popupHeight = 1650; var popupWin = LK.getAsset('fonmenu', { anchorX: 0.5, anchorY: 0.5, scaleX: popupWidth / 2048, scaleY: popupHeight / 2732 }); popupWin.x = WORLD_WIDTH / 2; popupWin.y = WORLD_HEIGHT / 2; popupWin.alpha = 0.98; upgradesPopup.addChild(popupWin); // Title removed as per request // Upgrade icons grid (3x3) - icons closer together var iconSize = 360; var availableWidth = popupWidth - 60; // Much smaller margin for even closer icons var availableHeight = popupHeight - 220; // Smaller margin for closer icons var cols = 3; var rows = 2; var iconSpacingX = availableWidth / (cols - 1); var iconSpacingY = availableHeight / (rows - 1); var gridStartX = WORLD_WIDTH / 2 - availableWidth / 2; var gridStartY = popupWin.y - popupHeight / 2 + 80 + 450; // Move top row down by 450 pixels (was 500) for (var i = 0; i < UPGRADE_DEFS.length; i++) { (function (i) { var upg = UPGRADE_DEFS[i]; var row = Math.floor(i / 3); var col = i % 3; // Move leftmost and rightmost icons closer to center var iconX = gridStartX + col * iconSpacingX; if (col === 0) { iconX += 300; // Move leftmost icon right by 300px (even closer to center) } if (col === 2) { iconX -= 300; // Move rightmost icon left by 300px (even closer to center) } var iconY = gridStartY + row * iconSpacingY; // Move bottom row up by 650 pixels (500 + 150) if (row === 1) { iconY = iconY - 650; } // Icon background/placeholder (will be replaced with actual upgrade icons later) var iconBg; if (upg.key === 'fireDamage') { iconBg = LK.getAsset('uron', { anchorX: 0.5, anchorY: 0.5, scaleX: iconSize / 360, // Assuming uron asset is 360x360 or similar aspect for this scaling scaleY: iconSize / 360 }); } else if (upg.key === 'fireRate') { iconBg = LK.getAsset('Skorost', { anchorX: 0.5, anchorY: 0.5, scaleX: iconSize / 360, // Skorost asset is 360x360 scaleY: iconSize / 360 }); } else { iconBg = LK.getAsset('test', { anchorX: 0.5, anchorY: 0.5, scaleX: iconSize / 360, scaleY: iconSize / 360 }); } iconBg.x = iconX; iconBg.y = iconY; upgradesPopup.addChild(iconBg); // Price text above icon var priceText = new Text2("$" + upg.cost(upgrades[upg.key]), { size: 108, // Increased by 1.5x (72 * 1.5) fill: 0x00FF00, align: 'center', font: 'Impact' }); priceText.anchor.set(0.5, 1); priceText.x = iconX; priceText.y = iconY - iconSize / 2 - 30; // Increased offset for larger icon upgradesPopup.addChild(priceText); // Level indicator below icon var levelText = new Text2(upgrades[upg.key] + "/" + upg.max, { size: 96, // Increased by 1.5x (64 * 1.5) fill: 0xFFFFFF, align: 'center', font: 'Impact' }); levelText.anchor.set(0.5, 0); levelText.x = iconX; levelText.y = iconY + iconSize / 2 + 30; // Increased offset for larger icon upgradesPopup.addChild(levelText); // Button interaction iconBg.interactive = true; iconBg.buttonMode = true; iconBg.upgradeKey = upg.key; iconBg.on('down', function (x, y, obj) { handleUpgradePurchase(upg, priceText, levelText); }); })(i); } // Add popup to game game.addChild(upgradesPopup); // Pause game logic (handled by LK automatically if popup is modal) upgradesButtonActive = true; upgradesButton.visible = false; } function closeUpgradesPopup() { if (upgradesPopup && upgradesPopup.parent) { upgradesPopup.parent.removeChild(upgradesPopup); LK.getSound('menu').play(); } // Restore normal speed to all enemies for (var i = 0; i < fallingObjects.length; i++) { var obj = fallingObjects[i]; if (!obj.isDestroyed && obj.originalSpeedY !== undefined) { tween(obj, { speedY: obj.originalSpeedY }, { duration: 200 }); // Restore original speed } } upgradesPopup = null; upgradesButtonActive = false; upgradesButton.visible = true; } // Handle upgrade purchase function handleUpgradePurchase(upg, priceText, levelText) { var level = upgrades[upg.key]; var cost = upg.cost(level); if (level >= upg.max) return; if (LK.getScore() < cost) return; // Apply upgrade LK.setScore(LK.getScore() - cost); upgrades[upg.key]++; // Play purchase sound LK.getSound('Pokupka').play(); // Apply effect applyUpgradeEffect(upg.key); updateScoreDisplay(); // Update price and level text priceText.setText("$" + upg.cost(upgrades[upg.key])); levelText.setText(upgrades[upg.key] + "/" + upg.max); // Optionally, flash or animate the texts LK.effects.flashObject(priceText, 0x00ff00, 200); LK.effects.flashObject(levelText, 0x00ff00, 200); // Find and pulse the upgrade icon that was purchased for (var i = 0; i < upgradesPopup.children.length; i++) { var child = upgradesPopup.children[i]; if (child.upgradeKey === upg.key) { // Pulse animation: scale up and then back down tween.stop(child, { scaleX: true, scaleY: true }); tween(child, { scaleX: child.scaleX * 1.15, scaleY: child.scaleY * 1.15 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(child, { scaleX: child.scaleX / 1.15, scaleY: child.scaleY / 1.15 }, { duration: 120, easing: tween.easeIn }); } }); break; } } } // Apply upgrade effect function applyUpgradeEffect(key) { if (key === 'fireDamage') { FIRE_DAMAGE = 35 + 20 * upgrades.fireDamage; } else if (key === 'fireRate') { FIRE_RATE_MS = 1000 - 176 * upgrades.fireRate; if (FIRE_RATE_MS < 120) FIRE_RATE_MS = 120; } else if (key === 'cityHeal') { if (cityInstance) { cityInstance.health = Math.min(cityInstance.health + 300, CITY_MAX_HEALTH); if (cityInstance.healthDisplay) { cityInstance.healthDisplay.setText('City Health: ' + cityInstance.health + ' / ' + CITY_MAX_HEALTH); } } } else if (key === 'shield') { // Temporary shield: make city invulnerable for 5 seconds if (cityInstance) { cityInstance.shielded = true; LK.effects.flashObject(cityInstance.graphic, 0x00ffff, 500); LK.setTimeout(function () { cityInstance.shielded = false; }, 5000); } } else if (key === 'turret') { // Enable auto-turret (fires at nearest target every 1.2s) if (!game.turretInterval) { game.turretInterval = LK.setInterval(function () { if (fallingObjects.length === 0) return; // Find nearest target var minDist = 99999, nearest = null; for (var i = 0; i < fallingObjects.length; i++) { var obj = fallingObjects[i]; if (!obj.isDestroyed) { var dx = obj.x - cityInstance.x; var dy = obj.y - (cityInstance.y - cityInstance.graphic.height / 2); var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; nearest = obj; } } } if (nearest) { nearest.takeDamage(FIRE_DAMAGE); LK.getSound('Shoot').play(); // Add a small effect at target var particle = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); particle.x = nearest.x; particle.y = nearest.y; game.addChild(particle); tween(particle, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 600, onFinish: function onFinish() { if (particle.parent) game.removeChild(particle); } }); } }, 1200); } } else if (key === 'crosshairSize') { if (crosshairInstance && crosshairInstance.graphic) { var scale = 1 + 0.18 * upgrades.crosshairSize; crosshairInstance.graphic.scaleX = scale; crosshairInstance.graphic.scaleY = scale; } } } // Button event - using LK event system for image asset upgradesButton.down = function (x, y, obj) { if (!upgradesButtonActive) showUpgradesPopup(); }; // Initialize Crosshair crosshairInstance = new Crosshair(); crosshairInstance.graphic = crosshairInstance.attachAsset('crosshair_asset', { anchorX: 0.5, anchorY: 0.5 }); crosshairInstance.speed = CROSSHAIR_SPEED; crosshairInstance.x = WORLD_WIDTH / 2; crosshairInstance.y = WORLD_HEIGHT / 2; game.addChild(crosshairInstance); // Initialize Virtual Joystick joystickInstance = new VirtualJoystick(); joystickInstance.initialize('joystick_base_asset', 'joystick_knob_asset', JOYSTICK_VISUAL_RADIUS, JOYSTICK_INTERACTION_RADIUS); game.addChild(joystickInstance); joystickInstance.visible = false; // Start hidden, will appear on first touch // Initialize Puha character in bottom left corner var puhaInstance = LK.getAsset('Puha', { anchorX: 0.0, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); puhaInstance.x = 450; // Left edge of puha positioned correctly from left edge, moved right by 400 puhaInstance.y = WORLD_HEIGHT - 50 - 2056 * 0.3 / 2 + 200; // Center of puha positioned correctly from bottom edge, moved down by 200 // Attach puha2 to puha instance var puha2Instance = LK.getAsset('Puha2', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); // Position puha2 at the muzzle location where particles appear var puhaWidth = 900 * 0.3; // Original width * scale var muzzleDistance = puhaWidth * 2; // Same distance calculation as muzzle flash puha2Instance.x = muzzleDistance; // Position at muzzle end relative to puha puha2Instance.y = 0; // Position relative to puha puhaInstance.addChild(puha2Instance); // Add puha before city so it appears behind city var cityIndex = game.children.indexOf(cityInstance); game.addChildAt(puhaInstance, cityIndex); // --- Event Handlers --- game.down = function (x, y, eventObj) { if (upgradesPopup) { // Block all input and pause game when popup is open return; } // Always show joystick at finger, and only allow one joystick at a time joystickInstance.x = x; joystickInstance.y = y; joystickInstance.visible = true; // Always activate joystick at this position, treat as local origin if (joystickInstance.processDown(x, y, eventObj, true)) { joystickTouchId = eventObj.identifier; isFiring = true; // Only allow firing while finger is held } }; game.move = function (x, y, eventObj) { if (upgradesPopup) { // Block all input and pause game when popup is open return; } if (joystickInstance.active && eventObj.identifier === joystickTouchId) { joystickInstance.processMove(x, y, eventObj); } // No crosshair update here; handled in game.update }; game.up = function (x, y, eventObj) { if (upgradesPopup) { // Block all input and pause game when popup is open return; } if (eventObj.identifier === joystickTouchId) { joystickInstance.processUp(eventObj); joystickInstance.visible = false; joystickTouchId = null; isFiring = false; } }; // --- Game Loop --- // --- Game Loop --- game.update = function () { // Block all game updates while upgrades popup is open if (upgradesPopup) { return; } var currentTime = LK.ticks * (1000 / 60); // Approximate current time in ms // 0. Update Crosshair Position based on Joystick if (crosshairInstance && joystickInstance) { // Calculate how far the knob is from the center (0..1) var knobDistance = Math.sqrt(joystickInstance.deltaX * joystickInstance.deltaX + joystickInstance.deltaY * joystickInstance.deltaY); // Crosshair speed is proportional to knob distance, up to CROSSHAIR_SPEED var effectiveSpeed = CROSSHAIR_SPEED * knobDistance * 2 / 1.5; crosshairInstance.x += joystickInstance.deltaX * effectiveSpeed; crosshairInstance.y += joystickInstance.deltaY * effectiveSpeed; // Clamp crosshair to screen bounds (considering anchor is 0.5, 0.5) var chHW = crosshairInstance.graphic.width / 2; var chHH = crosshairInstance.graphic.height / 2; crosshairInstance.x = Math.max(chHW, Math.min(WORLD_WIDTH - chHW, crosshairInstance.x)); crosshairInstance.y = Math.max(chHH, Math.min(WORLD_HEIGHT - chHH, crosshairInstance.y)); } // 1. Check if crosshair is over any target and change color accordingly var crosshairOverTarget = false; for (var k = 0; k < fallingObjects.length; k++) { var target = fallingObjects[k]; if (!target.isDestroyed && crosshairInstance.isOver(target)) { crosshairOverTarget = true; break; } } // Change crosshair color based on targeting if (crosshairOverTarget && crosshairInstance.graphic.tint !== 0xFF0000) { // Turn red when over target tween(crosshairInstance.graphic, { tint: 0xFF0000 }, { duration: 100, easing: tween.easeOut }); } else if (!crosshairOverTarget && crosshairInstance.graphic.tint !== 0xFFFFFF) { // Turn white when not over target tween(crosshairInstance.graphic, { tint: 0xFFFFFF }, { duration: 100, easing: tween.easeOut }); } // 2. Firing Logic // Only fire when crosshair is over target and firing is enabled if (crosshairOverTarget && currentTime >= lastFireTime + FIRE_RATE_MS) { lastFireTime = currentTime; var hitTargets = []; // Track which targets were hit this frame for (var k = 0; k < fallingObjects.length; k++) { var target = fallingObjects[k]; if (!target.isDestroyed && crosshairInstance.isOver(target)) { target.takeDamage(FIRE_DAMAGE); hitTargets.push(target); // Add to hit targets list // Create particle explosion at hit location var particleCount = 3 + Math.floor(Math.random() * 3); // 3 to 5 particles for (var p = 0; p < particleCount; p++) { var randomScale = 0.5 + Math.random() * 1.5; // Random scale between 0.5 and 2.0 var particle = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: randomScale, scaleY: randomScale }); particle.x = target.x; particle.y = target.y; game.addChild(particle); // Random direction and speed for each particle var angle = Math.random() * Math.PI * 2; var speed = 100 + Math.random() * 100; // Random speed between 100-200 var velocityX = Math.cos(angle) * speed; var velocityY = Math.sin(angle) * speed; // Animate particle movement and scale down tween(particle, { x: particle.x + velocityX, y: particle.y + velocityY, scaleX: 0, scaleY: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (particle.parent) { game.removeChild(particle); } } }); } } } // Play shoot sound once if any targets were hit if (hitTargets.length > 0) { LK.getSound('Shoot').play(); // Create muzzle flash particles at puha's muzzle end var muzzleParticleCount = 5 + Math.floor(Math.random() * 3); // 5 to 7 particles for (var mp = 0; mp < muzzleParticleCount; mp++) { var muzzleParticle = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0 + Math.random() * 2.0, // Random scale between 2.0 and 4.0 for larger smoke-like particles scaleY: 2.0 + Math.random() * 2.0 }); // Position particles at the muzzle end accounting for rotation // Puha is anchored at left side (0,0.5) and scaled to 0.3 var puhaWidth = 900 * 0.3; // Original width * scale var puhaHeight = 2056 * 0.3; // Original height * scale // Calculate muzzle position relative to puha's pivot point (left side, center) var muzzleDistance = puhaWidth * 2; // Distance from pivot to muzzle end - moved back 3 times closer var muzzleX = puhaInstance.x + Math.cos(puhaInstance.rotation) * muzzleDistance; var muzzleY = puhaInstance.y + Math.sin(puhaInstance.rotation) * muzzleDistance; muzzleParticle.x = muzzleX + (Math.random() - 0.5) * 20; // Small random spread muzzleParticle.y = muzzleY + (Math.random() - 0.5) * 20; game.addChild(muzzleParticle); // Animate muzzle particles in the direction puha is facing var muzzleAngle = puhaInstance.rotation + (Math.random() - 0.5) * 0.5; // Small angle variation var muzzleSpeed = 150 + Math.random() * 100; // Speed between 150-250 var muzzleVelX = Math.cos(muzzleAngle) * muzzleSpeed; var muzzleVelY = Math.sin(muzzleAngle) * muzzleSpeed; tween(muzzleParticle, { x: muzzleParticle.x + muzzleVelX, y: muzzleParticle.y + muzzleVelY, scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (muzzleParticle.parent) { game.removeChild(muzzleParticle); } } }); } } } // 3. Update twinkling stars updateStars(); // 4. Spawn new falling objects if (currentTime >= nextSpawnTime) { spawnFallingObject(); nextSpawnTime = currentTime + SPAWN_INTERVAL_MS_MIN + Math.random() * (SPAWN_INTERVAL_MS_MAX - SPAWN_INTERVAL_MS_MIN); } // 5. Update and check falling objects for (var i = fallingObjects.length - 1; i >= 0; i--) { var obj = fallingObjects[i]; if (obj.isDestroyed) { // Remove from game immediately when destroyed if (obj.parent) { game.removeChild(obj); } fallingObjects.splice(i, 1); continue; } obj.update(); // Calculate the Y coordinate of the "middle" of the city image var cityMiddleY = cityInstance.y - cityInstance.graphic.height / 2; // Check if the falling object has reached the middle of the city if (obj.y + obj.graphic.height * 0.5 * (obj.graphic.scaleY || 1) >= cityMiddleY) { var damageToCity = obj.objectType === 'meteor' ? METEOR_DAMAGE_TO_CITY : ALIEN_DAMAGE_TO_CITY; cityInstance.takeDamage(damageToCity); LK.getSound('Boom').play(); // Play boom sound when object hits city obj.destroySelf(); continue; } if (obj.y - obj.graphic.height * 0.5 * (obj.graphic.scaleY || 1) > WORLD_HEIGHT) { obj.destroySelf(); continue; } } // Old safety check for playerHoldingTarget is removed as that system is gone. // Update puha to track crosshair if (puhaInstance && crosshairInstance) { // Calculate angle from puha to crosshair var dx = crosshairInstance.x - puhaInstance.x; var dy = crosshairInstance.y - puhaInstance.y; var angle = Math.atan2(dy, dx); // Rotate puha to face crosshair puhaInstance.rotation = angle; } // Always ensure crosshair is rendered above all falling objects if (crosshairInstance && crosshairInstance.parent === game) { game.removeChild(crosshairInstance); game.addChild(crosshairInstance); } };
===================================================================
--- original.js
+++ change.js
@@ -980,9 +980,12 @@
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
-puha2Instance.x = 600; // Position relative to puha - moved further to right edge
+// Position puha2 at the muzzle location where particles appear
+var puhaWidth = 900 * 0.3; // Original width * scale
+var muzzleDistance = puhaWidth * 2; // Same distance calculation as muzzle flash
+puha2Instance.x = muzzleDistance; // Position at muzzle end relative to puha
puha2Instance.y = 0; // Position relative to puha
puhaInstance.addChild(puha2Instance);
// Add puha before city so it appears behind city
var cityIndex = game.children.indexOf(cityInstance);
Метеорит без огня пастельные цвета In-Game asset. 2d. High contrast. No shadows
Похожий
Иконка повышение урона, сочные цвета. In-Game asset. 2d. High contrast. No shadows. Comix
иконка на скорость атаки
надпись upgrade как красивая кнопка In-Game asset. 2d. High contrast. No shadows. comix
центральный круг желтый а внешний оранжевый
голубой вместо оранжевого
Красно оранжевый
Restyled
Разрешение 2048 на 400
молния должна быть с двух концов одинаковая и ответвления смотреть строго вверх и вниз а не наискосок
иконка шанса двойного урона (x2)
иконка голубой молнии без текста и цыферблата
иконка огня
Вместо молнии синяя снежинка, все остальное без изменений
сделать светлее
Комикс
сделать рамку толще в два раза и немного не правильной формы как в комиксах
сделать рамку тоньше сохранив стиль и цвета сочнее
надпись shop как красивая кнопка In-Game asset. 2d. High contrast. No shadows. comix
Рамка для всплывающей меню подсказки. In-Game asset. 2d. High contrast. No shadows
Крестик для закрытия окна. In-Game asset. 2d. High contrast. No shadows
Иконка английского языка флаг без текста In-Game asset. 2d. High contrast. No shadows
Заменить на российский без текста, рамку сохранить
Удалить желтый фон
Флаг земенить на немецкий рамки сохранить
Заменить на испанский, сохранить рамку.
сделать точно такуюже рамку но надпись заменить на shop. звезду заменить на ракету, а стрелку на щит
все оставить как есть но удалить черноту за рамками
круглая иконка подсказки I. In-Game asset. 2d. High contrast. No shadows
убери все звезды оставь только чистое небо
иконка восстановление здоровья много зеленых крестов в рамке, сочные цвета красивый фон. In-Game asset. 2d. High contrast. No shadows
синий щит на ярко оранжевом фоне
залп ракетного огня
шаровая молния. In-Game asset. 2d. High contrast. No shadows
башня тесла с молниями фон голубой
Огненный шар
перекрасить больше желтого и оранжевого
перекрасить больше голубого, светло-голубого,
турецкий флаг
Вместо огненного кольца, огненные шары разлетающие вверх в разные стороны
Текст убрать. Вместо молний снежинки
Вместо молнии снежинка, и покрасить в синий
Льдинка как стеклышко. In-Game asset. 2d. High contrast. No shadows
убрать дырку
бесформенная амеба
удали крывлья оставь только жука
оставь только крылья, удали жука
перекрась
Shoot
Sound effect
Boom
Sound effect
Pokupka
Sound effect
menu
Sound effect
molnia
Sound effect
krit
Sound effect
icetresk
Sound effect
peretik
Sound effect
music1
Music
music2
Music
music3
Music
musicFight
Music
udarshield
Sound effect
startraket
Sound effect
raketaudar
Sound effect
Ognemet
Sound effect
Tresklda
Sound effect
stop
Sound effect
goldsound
Sound effect
alien_bum
Sound effect