User prompt
change game name to "What The Duck"
User prompt
the information text in the rules must be red
User prompt
make "Rules" text colour white
User prompt
give the game rules information to players i a new menu part called "rules"
User prompt
Add variety to ducks by introducing 3 different duck types: 1. Golden Duck: - Rare and very fast. - Gives +50 points when hit. 2. Armored Duck: - Needs to be hit twice to fall. - Flash red after first hit. 3. Mini Fast Duck: - Smaller in size and faster than regular ducks. - Worth +25 points. Use different sprites and point values for each duck type. Make sure the duck types appear with increasing frequency in higher levels.
User prompt
Add variety to ducks by introducing 3 different duck types: 1. Golden Duck: - Rare and very fast. - Gives +50 points when hit. 2. Armored Duck: - Needs to be hit twice to fall. - Flash red after first hit. 3. Mini Fast Duck: - Smaller in size and faster than regular ducks. - Worth +25 points. Use different sprites and point values for each duck type. Make sure the duck types appear with increasing frequency in higher levels.
User prompt
Add 3 special power-up weapons that players can unlock or collect: 1. Double Barrel Shotgun: - Shoots a wider bullet spread, hitting 2 ducks at once if they’re close. 2. Slow Time Power-up: - Slows down all ducks for 5 seconds. - Use a retro visual effect during slowdown. 3. Auto-Aim Power-up: - Automatically hits the nearest duck for 3 seconds. - Makes shooting easier in tough levels. Allow these power-ups to appear randomly or be earned by high performance. Show icons for active power-ups on screen with countdown timers.
User prompt
Add 2 new game modes to the game in addition to the classic one: 1. Time Attack Mode: - The player has 30 seconds to hit as many ducks as possible. - Show a countdown timer on the screen. - Final score is based on the number of ducks hit. 2. Endless Mode: - Ducks spawn infinitely. - Bullets are unlimited. - The goal is to hit as many ducks as possible without missing 3 times in a row. - If the player misses 3 times in a row, the game ends. Let the player choose a mode from a main menu before starting the game.
User prompt
give level 1 10 bullets ,give level 2 15 bullets,give level 3 20 bullets, give level 4 30 bullets, give level 5 32 bullets
User prompt
first levels have 6 ducks,level 2 have 12 ducks,level 3 have 20 ducks, first levels have 6 ducks, first level 4 have 30 ducks,level 5 have 40 ducks
User prompt
first levels have 6 ducks,level 2 have 12 ducks,level 3 have 20 ducks, first levels have 6 ducks, first level 4 have 30 ducks,level 5 have 6 ducks
User prompt
level 1 shoot at least 3 beards,level 2 shoot at least 6 beards, level 3 shoot at least 12 beards,level 4 shoot at least 18 beards, level 1 shoot at least 20 beards
User prompt
give level 1 10 bullets ,give level 2 15 bullets,give level 3 20 bullets, give level 4 25 bullets, give level 5 30 bullets
User prompt
level 2,3,4,5 give 15 bullets only
User prompt
fix that extra bullet to 15. not 20!!!
User prompt
first level player must have 15 bullets and each level give player 15 more bullets
User prompt
add new bird to hunt and new levels
User prompt
every level add 20 bullet to player
User prompt
show the bullet amount as a text under the level text
User prompt
after bullets amount is 0 game over
User prompt
after 20 bullet missed say player to warning message
User prompt
What to Fix: Hitting ducks should always work: Even after 1 or more missed shots, the player should still be able to hit ducks. Misses must not disable collision: Make sure that missing does not deactivate the hit detection or event listeners on ducks. Check that each duck’s hitbox remains active throughout its flight. Reset state properly between shots: Ensure the shooting system resets correctly after each shot — whether it’s a hit or miss. Do not block interaction with ducks unless bullets are zero. Ammo Logic: Player can keep shooting until bullets reach 0. Misses should only reduce bullet count, nothing else.
User prompt
add "bullet" text for the bullets amount
User prompt
show the bullets amount under the screen
User prompt
🔫 Ammo Rules: Initial Ammo: On Level 1, give the player 20 bullets. Progressive Ammo per New Game: When the game restarts, the player gets: remaining_bullets_from_last_game + 20 For example: If the player had 5 bullets left when the game ended, the next game starts with 25 bullets. Shooting Logic: Every time the player shoots (whether they hit or miss), subtract 1 bullet. If bullets = 0, prevent shooting until the next game. 📺 Display Ammo Count: Show the current number of bullets on the screen, aligned at the bottom center or bottom left. Use a clear retro-style pixel font or simple icon with text: Example: "Bullets: 18" Update this display live whenever the player shoots.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ // Achievement popup class var AchievementPopup = Container.expand(function () { var self = Container.call(this); var txt = new Text2('', { size: 120, fill: 0xFFD700 }); txt.anchor.set(0.5, 0.5); self.addChild(txt); self.visible = false; self.show = function (message, color) { txt.setText(message); if (txt && txt.style) { txt.style.fill = color || 0xFFD700; } self.x = 2048 / 2; self.y = 2732 / 2 - 300; self.visible = true; self.alpha = 1; tween(self, { alpha: 0 }, { duration: 1200, onFinish: function onFinish() { self.visible = false; } }); }; return self; }); // Dog class var Dog = Container.expand(function () { var self = Container.call(this); var dogAsset = self.attachAsset('dog', { anchorX: 0.5, anchorY: 1 }); self.visible = false; // Show dog at (x, y) self.show = function (x, y) { self.x = x; self.y = y; self.visible = true; dogAsset.visible = true; if (self.laughAsset) self.laughAsset.visible = false; // Optionally, you could add a little "pop up" animation here // For example, tween the dog from below the screen to y var startY = 2732 + dogAsset.height; self.y = startY; tween(self, { y: y }, { duration: 350, easing: tween.cubicOut }); }; // Hide dog self.hide = function () { // Animate dog going down before hiding var endY = 2732 + (dogAsset.height || 180); tween(self, { y: endY }, { duration: 350, easing: tween.cubicIn, onFinish: function onFinish() { self.visible = false; dogAsset.visible = false; } }); }; // Show laugh animation self.laugh = function (x, y) { self.x = x; self.y = y; self.visible = true; dogAsset.visible = false; if (!self.laughAsset) { self.laughAsset = self.attachAsset('dog_laugh', { anchorX: 0.5, anchorY: 1 }); } self.laughAsset.visible = true; LK.getSound('dog_laugh').play(); // Hide after 1.2s LK.setTimeout(function () { self.laughAsset.visible = false; self.hide(); }, 1200); }; return self; }); // Duck class var Duck = Container.expand(function () { var self = Container.call(this); // Duck type: normal, fast, golden, armored, mini // Type selection is handled by spawn logic, but fallback to random if not set self.type = self.type || 'normal'; // Duck asset selection var duckAsset = null; var duckColors = ['duck_green', 'duck_blue', 'duck_red']; // If type is not set, pick by probability (for backward compatibility) if (!self.type || self.type === 'normal' || self.type === 'fast') { // 1 in 12 chance for golden duck if (Math.random() < 1 / 12) { self.type = 'golden'; } else if (Math.random() < 1 / 10) { self.type = 'armored'; } else if (Math.random() < 1 / 7) { self.type = 'mini'; } else if (Math.random() < 0.25) { self.type = 'fast'; } else { self.type = 'normal'; } } // Asset and property assignment by type if (self.type === 'golden') { duckAsset = self.attachAsset('duck_golden', { anchorX: 0.5, anchorY: 0.5 }); duckAsset.tint = 0xFFD700; // gold tint self.speed = 10 + Math.random() * 2.5; // very fast self.points = 50; self.hitPoints = 1; self.sizeScale = 1.0; } else if (self.type === 'armored') { duckAsset = self.attachAsset('duck_armored', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4 + Math.random() * 2; // slower self.points = 20; self.hitPoints = 2; self.sizeScale = 1.0; self.armorHit = 0; } else if (self.type === 'mini') { duckAsset = self.attachAsset('duck_mini', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8 + Math.random() * 2.5; // fast self.points = 25; self.hitPoints = 1; self.sizeScale = 0.55; } else if (self.type === 'fast') { var colorIdx = Math.floor(Math.random() * duckColors.length); duckAsset = self.attachAsset(duckColors[colorIdx], { anchorX: 0.5, anchorY: 0.5 }); self.speed = 7 + Math.random() * 2.5; self.points = 15; self.hitPoints = 1; self.sizeScale = 1.0; } else { // normal var colorIdx = Math.floor(Math.random() * duckColors.length); duckAsset = self.attachAsset(duckColors[colorIdx], { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5 + Math.random() * 2; self.points = 10; self.hitPoints = 1; self.sizeScale = 1.0; } // Scale asset for mini duck if (self.sizeScale !== 1.0) { duckAsset.scaleX = duckAsset.scaleY = self.sizeScale; } self.angle = 0; // radians, will be set by level self.alive = true; self.hit = false; self.escaped = false; // For animation self.flyTween = null; // --- Hitbox properties for all ducks (full sprite, slightly larger for fast/golden/mini ducks) --- self.getHitboxRadius = function () { var baseRadius = Math.max(duckAsset.width, duckAsset.height) * 0.5 * (self.sizeScale || 1.0); if (self.type === 'fast' || self.type === 'golden' || self.type === 'mini' || self.speed >= 7) { return baseRadius * 0.7; } return baseRadius * 0.55; }; // Head hitbox (top 1/3 of duck) self.isHeadshot = function (x, y) { var dx = self.x - x; var dy = self.y - y; var dist = Math.sqrt(dx * dx + dy * dy); var headRadius = duckAsset.height * 0.18 * (self.sizeScale || 1.0); var headCenterY = self.y - duckAsset.height * 0.22 * (self.sizeScale || 1.0); var headDist = Math.sqrt((self.x - x) * (self.x - x) + (headCenterY - y) * (headCenterY - y)); return headDist < headRadius; }; // Called every tick self.update = function () { if (!self.alive || self.hit) return; self.x += Math.cos(self.angle) * self.speed; self.y += Math.sin(self.angle) * self.speed; if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) { self.escaped = true; self.alive = false; } }; // Animate duck falling when hit self.fall = function (_onFinish) { self.alive = false; tween(self, { rotation: Math.PI * 1.5, y: self.y + 400 }, { duration: 700, easing: tween.cubicIn, onFinish: function onFinish() { if (_onFinish) _onFinish(); } }); }; // For armored duck: flash red after first hit self.flashArmor = function () { if (duckAsset) { LK.effects.flashObject(duckAsset, 0xff2222, 300); } }; return self; }); // Power-up collectible class var PowerupCollectible = Container.expand(function () { var self = Container.call(this); self.type = null; self.icon = null; self.radius = 60; self.speedY = -2 - Math.random() * 2; self.lifetime = 0; self.maxLifetime = 400; // ~6 seconds self.init = function (ptype, x, y) { self.type = ptype; var iconId = ptype === 'doubleBarrel' ? 'powerup_double' : ptype === 'slowTime' ? 'powerup_slow' : 'powerup_auto'; self.icon = self.attachAsset(iconId, { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.icon.scaleX = self.icon.scaleY = 1.1; self.visible = true; self.alpha = 0.92; }; self.update = function () { self.y += self.speedY; self.lifetime++; // Fade out near end of life if (self.lifetime > self.maxLifetime - 60) { self.alpha = Math.max(0, (self.maxLifetime - self.lifetime) / 60); } // Remove if out of bounds or expired if (self.y < 100 || self.lifetime > self.maxLifetime) { if (self.parent) self.parent.removeChild(self); if (typeof powerupCollectibles !== "undefined") { var idx = powerupCollectibles.indexOf(self); if (idx >= 0) powerupCollectibles.splice(idx, 1); } } }; // Check if tap is within radius self.isHit = function (x, y) { var dx = self.x - x; var dy = self.y - y; return dx * dx + dy * dy < self.radius * self.radius; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb // sky blue }); /**** * Game Code ****/ // Use red duck as base for mini // Use blue duck as base for armored // Use red duck as base for golden // Power-up icons and retro overlay // Music // Sounds // sky blue // Ducks: 3 colors, 1 dog, 1 bullet, 1 background, 1 crosshair, 1 "laugh" dog // --- Power-up collectibles array --- var powerupCollectibles = []; // Spawn a power-up collectible at a random position function spawnPowerupCollectible(ptype) { var px = 200 + Math.random() * (2048 - 400); var py = 2732 - 200 - Math.random() * 800; var p = new PowerupCollectible(); p.init(ptype, px, py); powerupCollectibles.push(p); game.addChild(p); } // Activate a power-up function activatePowerup(ptype) { if (powerupActive[ptype]) return; powerupActive[ptype] = true; var duration = 0; if (ptype === 'doubleBarrel') duration = 7; if (ptype === 'slowTime') duration = 5; if (ptype === 'autoAim') duration = 3; powerupTimers[ptype] = duration; showPowerupIcon(ptype, duration); // Apply effect if (ptype === 'slowTime') { // Slow all ducks for (var i = 0; i < ducks.length; i++) { ducks[i].speed = ducks[i].speed * 0.35; } // Add retro overlay if (!slowTimeEffectOverlay) { slowTimeEffectOverlay = LK.getAsset('retro_overlay', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); slowTimeEffectOverlay.alpha = 0.22; game.addChild(slowTimeEffectOverlay); } } if (ptype === 'autoAim') { // No immediate effect, handled in shooting logic } if (ptype === 'doubleBarrel') { // No immediate effect, handled in shooting logic } // Set timeout to deactivate if (powerupTimeouts[ptype]) LK.clearTimeout(powerupTimeouts[ptype]); powerupTimeouts[ptype] = LK.setTimeout(function () { powerupActive[ptype] = false; powerupTimers[ptype] = 0; hidePowerupIcon(ptype); if (ptype === 'slowTime') { // Restore duck speed for (var i = 0; i < ducks.length; i++) { ducks[i].speed = ducks[i].speed / 0.35; } // Remove overlay if (slowTimeEffectOverlay && slowTimeEffectOverlay.parent) { slowTimeEffectOverlay.parent.removeChild(slowTimeEffectOverlay); slowTimeEffectOverlay = null; } } }, duration * 1000); } // Show power-up icon in GUI function showPowerupIcon(ptype, duration) { var iconId = ptype === 'doubleBarrel' ? 'powerup_double' : ptype === 'slowTime' ? 'powerup_slow' : 'powerup_auto'; var icon = LK.getAsset(iconId, { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); icon.scaleX = icon.scaleY = 1.1; var timerTxt = new Text2(duration + "s", { size: 60, fill: 0xffffff }); timerTxt.anchor.set(0.5, 0.5); icon.addChild(timerTxt); powerupIcons[ptype] = icon; // Place icons in top left, but not in 0-100px (menu area) var idx = ptype === 'doubleBarrel' ? 0 : ptype === 'slowTime' ? 1 : 2; icon.x = 120 + idx * 120; icon.y = 120; LK.gui.top.addChild(icon); icon._timerTxt = timerTxt; } // Hide power-up icon function hidePowerupIcon(ptype) { if (powerupIcons[ptype] && powerupIcons[ptype].parent) { powerupIcons[ptype].parent.removeChild(powerupIcons[ptype]); powerupIcons[ptype] = null; } } // Game state variables var ducks = []; var bullets = []; var level = 1; var ducksPerLevel = 5; var ducksToHit = 0; var ducksHit = 0; var ducksEscaped = 0; var maxBullets = 3; var bulletsLeft = 3; var timeLimit = 15; // seconds var timeLeft = 15; var gameActive = false; var waveActive = false; var highScore = storage.highScore || 0; var score = 0; var dog = null; var crosshair = null; var timerInterval = null; var levelText = null; var scoreText = null; var timerText = null; var bulletIcons = []; var waveResultTimeout = null; // Track missed shots for warning var missedShots = 0; var missedWarningShown = false; // --- Power-up State --- var powerupActive = { doubleBarrel: false, slowTime: false, autoAim: false }; var powerupTimers = { doubleBarrel: 0, slowTime: 0, autoAim: 0 }; var powerupIcons = { doubleBarrel: null, slowTime: null, autoAim: null }; var powerupTimeouts = { doubleBarrel: null, slowTime: null, autoAim: null }; // For slow time effect var slowTimeEffectOverlay = null; // --- Game Mode State --- var GAME_MODE_CLASSIC = "classic"; var GAME_MODE_TIMEATTACK = "timeattack"; var GAME_MODE_ENDLESS = "endless"; var gameMode = null; // Set by menu var mainMenuContainer = null; var endlessMissStreak = 0; var endlessGameOver = false; // Background var bg = LK.getAsset('bg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(bg); // --- Main Menu UI --- function showMainMenu() { // Remove previous menu if present if (mainMenuContainer && mainMenuContainer.parent) { mainMenuContainer.parent.removeChild(mainMenuContainer); } mainMenuContainer = new Container(); // Dim background var menuBg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 16, scaleY: 16, x: 2048 / 2, y: 2732 / 2 }); menuBg.alpha = 0.85; mainMenuContainer.addChild(menuBg); // Title var title = new Text2("Duck Hunt", { size: 220, fill: 0xFFD700, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 400; mainMenuContainer.addChild(title); // Classic Mode Button var btnClassic = new Text2("Classic Mode", { size: 120, fill: 0xFFFFFF, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); btnClassic.anchor.set(0.5, 0.5); btnClassic.x = 2048 / 2; btnClassic.y = 900; mainMenuContainer.addChild(btnClassic); // Time Attack Button var btnTime = new Text2("Time Attack", { size: 120, fill: 0xFFFFFF, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); btnTime.anchor.set(0.5, 0.5); btnTime.x = 2048 / 2; btnTime.y = 1100; mainMenuContainer.addChild(btnTime); // Endless Mode Button var btnEndless = new Text2("Endless Mode", { size: 120, fill: 0xFFFFFF, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); btnEndless.anchor.set(0.5, 0.5); btnEndless.x = 2048 / 2; btnEndless.y = 1300; mainMenuContainer.addChild(btnEndless); // Instructions var info = new Text2("Choose a mode to start!", { size: 80, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); info.anchor.set(0.5, 0.5); info.x = 2048 / 2; info.y = 1550; mainMenuContainer.addChild(info); // Button handlers btnClassic.interactive = true; btnClassic.down = function () { gameMode = GAME_MODE_CLASSIC; if (mainMenuContainer.parent) mainMenuContainer.parent.removeChild(mainMenuContainer); startLevel(); }; btnTime.interactive = true; btnTime.down = function () { gameMode = GAME_MODE_TIMEATTACK; if (mainMenuContainer.parent) mainMenuContainer.parent.removeChild(mainMenuContainer); startLevel(); }; btnEndless.interactive = true; btnEndless.down = function () { gameMode = GAME_MODE_ENDLESS; if (mainMenuContainer.parent) mainMenuContainer.parent.removeChild(mainMenuContainer); startLevel(); }; // Add to game game.addChild(mainMenuContainer); } // Show menu on load showMainMenu(); // Dog dog = new Dog(); game.addChild(dog); // Achievement popup (always on top) var achievementPopup = new AchievementPopup(); game.addChild(achievementPopup); // Crosshair crosshair = LK.getAsset('crosshair', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 }); crosshair.visible = false; game.addChild(crosshair); // GUI: Level, Score, Timer, Bullets levelText = new Text2('Level 1', { size: 90, fill: 0xFFF000 }); levelText.anchor.set(1, 0); // right align to match high score scoreText = new Text2('Score: 0', { size: 90, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); timerText = new Text2('Time: 15', { size: 90, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // High Score UI var highScoreText = new Text2('High: ' + highScore, { size: 70, fill: 0xFFD700 }); highScoreText.anchor.set(1, 0); // Place high score in top right LK.gui.topRight.addChild(highScoreText); // Place level text directly under high score text in top right LK.gui.topRight.addChild(levelText); // Position GUI // Position GUI scoreText.x = 2048 / 2; timerText.x = 2048 * 3 / 4; highScoreText.x = 0; highScoreText.y = 0; levelText.x = 0; levelText.y = highScoreText.height + 10; // 10px gap below high score // Responsive UI: adjust font size and icon size for mobile function resizeUI() { var w = LK.width || 2048; var h = LK.height || 2732; var scale = Math.min(w / 2048, h / 2732); if (scoreText && scoreText.style) scoreText.style.size = 90 * scale; if (timerText && timerText.style) timerText.style.size = 90 * scale; if (highScoreText && highScoreText.style) highScoreText.style.size = 70 * scale; if (levelText && levelText.style) levelText.style.size = 90 * scale; for (var i = 0; i < bulletIcons.length; i++) { bulletIcons[i].scaleX = bulletIcons[i].scaleY = scale; } } LK.on('resize', resizeUI); resizeUI(); // Bullets GUI (now under the level text in the top right) // Show both bullet icons (bottom) and a text counter (top right under level) var bulletCountText = null; var bulletLabelText = null; function updateBulletIcons() { // Remove old icons for (var i = 0; i < bulletIcons.length; i++) { if (bulletIcons[i].parent) bulletIcons[i].parent.removeChild(bulletIcons[i]); } bulletIcons = []; // Draw new icons (up to 10 for visual clarity, then show text) var maxIcons = Math.min(10, bulletsLeft); // Center the icons under the screen var iconSpacing = 60; var totalWidth = (maxIcons - 1) * iconSpacing; var startX = 2048 / 2 - totalWidth / 2; for (var i = 0; i < maxIcons; i++) { var icon = LK.getAsset('bullet_icon', { anchorX: 0.5, anchorY: 0.5, x: startX + i * iconSpacing, y: 2732 - 120 }); LK.gui.bottom.addChild(icon); bulletIcons.push(icon); } // Remove old text if present if (bulletCountText && bulletCountText.parent) { bulletCountText.parent.removeChild(bulletCountText); } if (bulletLabelText && bulletLabelText.parent) { bulletLabelText.parent.removeChild(bulletLabelText); } // Add "Bullets" label above the count, now in top right under level bulletLabelText = new Text2("Bullets", { size: 60, fill: 0xFFF000, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); bulletLabelText.anchor.set(1, 0); // right align, top anchor bulletLabelText.x = 0; bulletLabelText.y = levelText.y + levelText.height + 10; LK.gui.topRight.addChild(bulletLabelText); // Show bullet count as text (always, for clarity) bulletCountText = new Text2("" + bulletsLeft, { size: 90, fill: 0xFFF000, font: "'PressStart2P','GillSans-Bold',Impact,'Arial Black',Tahoma" }); bulletCountText.anchor.set(1, 0); // right align, top anchor bulletCountText.x = 0; bulletCountText.y = bulletLabelText.y + bulletLabelText.height + 2; LK.gui.topRight.addChild(bulletCountText); } // Start a new level function startLevel() { ducks = []; bullets = []; ducksHit = 0; ducksEscaped = 0; missedShots = 0; missedWarningShown = false; endlessMissStreak = 0; endlessGameOver = false; // --- Mode-specific setup --- if (gameMode === GAME_MODE_CLASSIC || !gameMode) { // Set ducksPerLevel and ducksToHit per level as per requirements if (level === 1) { ducksPerLevel = 6; ducksToHit = 3; } else if (level === 2) { ducksPerLevel = 12; ducksToHit = 6; } else if (level === 3) { ducksPerLevel = 20; ducksToHit = 12; } else if (level === 4) { ducksPerLevel = 30; ducksToHit = 18; } else if (level === 5) { ducksPerLevel = 40; ducksToHit = 20; } else { ducksPerLevel = Math.min(25, 15 + (level - 5) * 2); ducksToHit = Math.ceil(ducksPerLevel * 0.7); } // Set bulletsLeft per level: 10 for level 1, 15 for level 2, 20 for level 3, 30 for level 4, 32 for level 5 if (level === 1) { bulletsLeft = 10; } else if (level === 2) { bulletsLeft = 15; } else if (level === 3) { bulletsLeft = 20; } else if (level === 4) { bulletsLeft = 30; } else if (level === 5) { bulletsLeft = 32; } else { bulletsLeft = 32 + 2 * (level - 5); } maxBullets = bulletsLeft; timeLimit = Math.max(8, 15 - (level - 1)); timeLeft = timeLimit; levelText.setText('Level ' + level); } else if (gameMode === GAME_MODE_TIMEATTACK) { // 30 seconds, infinite ducks, infinite bullets, score = ducks hit ducksPerLevel = 99999; ducksToHit = 0; bulletsLeft = 99999; maxBullets = bulletsLeft; timeLimit = 30; timeLeft = 30; levelText.setText('Time Attack'); } else if (gameMode === GAME_MODE_ENDLESS) { // Infinite ducks, infinite bullets, game ends on 3 misses in a row ducksPerLevel = 99999; ducksToHit = 0; bulletsLeft = 99999; maxBullets = bulletsLeft; timeLimit = 99999; timeLeft = 99999; levelText.setText('Endless'); } gameActive = true; waveActive = true; updateBulletIcons(); scoreText.setText('Score: ' + score); timerText.setText('Time: ' + timeLeft); // Duck spawn schedule and speed per level/mode var duckSpawnTimer = null; var ducksSpawned = 0; var ducksAtOnce = 1; var duckSpawnInterval = 3000; var duckSpeedMin = 2; var duckSpeedMax = 3.5; if (gameMode === GAME_MODE_CLASSIC || !gameMode) { if (level === 1) { ducksAtOnce = 1; duckSpawnInterval = 3000; duckSpeedMin = 2; duckSpeedMax = 3.5; } else if (level === 2) { ducksAtOnce = 1; duckSpawnInterval = 2000; duckSpeedMin = 3.5; duckSpeedMax = 5; } else if (level === 3) { ducksAtOnce = 2; duckSpawnInterval = 1800; duckSpeedMin = 4.2; duckSpeedMax = 5.8; } else if (level === 4) { ducksAtOnce = 3; duckSpawnInterval = 1500; duckSpeedMin = 5.5; duckSpeedMax = 7.2; } else if (level === 5) { ducksAtOnce = 4; duckSpawnInterval = 1200; duckSpeedMin = 6.5; duckSpeedMax = 8.5; } else { ducksAtOnce = 5; duckSpawnInterval = 1000; duckSpeedMin = 7.5 + (level - 5) * 0.5; duckSpeedMax = 9.5 + (level - 5) * 0.7; } } else if (gameMode === GAME_MODE_TIMEATTACK) { ducksAtOnce = 2; duckSpawnInterval = 900; duckSpeedMin = 4.5; duckSpeedMax = 7.5; } else if (gameMode === GAME_MODE_ENDLESS) { ducksAtOnce = 2; duckSpawnInterval = 900; duckSpeedMin = 4.5; duckSpeedMax = 7.5; } // Clear any previous duck spawn timer if (typeof duckSpawnTimer !== "undefined" && duckSpawnTimer) { LK.clearInterval(duckSpawnTimer); duckSpawnTimer = null; } ducksSpawned = 0; // Function to spawn ducks and power-ups function spawnDucks() { if (!gameActive || !waveActive) return; if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && ducksSpawned >= ducksPerLevel) { if (duckSpawnTimer) { LK.clearInterval(duckSpawnTimer); duckSpawnTimer = null; } return; } var ducksThisWave = gameMode === GAME_MODE_CLASSIC || !gameMode ? Math.min(ducksAtOnce, ducksPerLevel - ducksSpawned) : ducksAtOnce; for (var d = 0; d < ducksThisWave; d++) { // Determine duck type based on level and random chance var duckType = 'normal'; var goldenChance = 0.04 + 0.01 * Math.min(level, 10); // 4% + 1% per level, max 14% var armoredChance = 0.06 + 0.02 * Math.max(level - 2, 0); // 6% + 2% per level after 2 var miniChance = 0.08 + 0.02 * Math.max(level - 3, 0); // 8% + 2% per level after 3 var r = Math.random(); if (r < goldenChance) { duckType = 'golden'; } else if (r < goldenChance + armoredChance) { duckType = 'armored'; } else if (r < goldenChance + armoredChance + miniChance) { duckType = 'mini'; } else if (level >= 2 && r < goldenChance + armoredChance + miniChance + 0.15) { duckType = 'fast'; } else { duckType = 'normal'; } var duck = new Duck(); duck.type = duckType; // Randomize start edge: 0=left, 1=right, 2=bottom var edge = Math.floor(Math.random() * 3); var startX, startY, angle; if (edge === 0) { // left startX = -60; startY = 400 + Math.random() * (2732 - 800); angle = Math.random() * Math.PI / 3 - Math.PI / 6; // -30 to +30 deg } else if (edge === 1) { // right startX = 2048 + 60; startY = 400 + Math.random() * (2732 - 800); angle = Math.PI + (Math.random() * Math.PI / 3 - Math.PI / 6); // 150 to 210 deg } else { // bottom startX = 200 + Math.random() * (2048 - 400); startY = 2732 + 60; angle = -Math.PI / 2 + (Math.random() * Math.PI / 4 - Math.PI / 8); // -67 to -23 deg } duck.x = startX; duck.y = startY; duck.angle = angle; // Duck speed is set in Duck class by type, but override for level scaling if (duck.type === 'golden') { duck.speed = Math.max(duck.speed, duckSpeedMax + 2); } else if (duck.type === 'mini') { duck.speed = Math.max(duck.speed, duckSpeedMax + 1); } else if (duck.type === 'fast') { duck.speed = Math.max(duck.speed, duckSpeedMax); } else if (duck.type === 'armored') { duck.speed = Math.min(duck.speed, duckSpeedMin + 1.5); } else { duck.speed = duckSpeedMin + Math.random() * (duckSpeedMax - duckSpeedMin); } ducks.push(duck); // Always add ducks above background and below crosshair/dog/UI game.addChild(duck); // Move dog and crosshair to top of display list if present if (dog && dog.parent) { dog.parent.removeChild(dog); game.addChild(dog); } if (crosshair && crosshair.parent) { crosshair.parent.removeChild(crosshair); game.addChild(crosshair); } LK.getSound('duck_fly').play(); ducksSpawned++; } // --- Power-up spawn logic --- // Power-ups can appear randomly (1 in 7 chance per spawn, but not if already active) if (Math.random() < 1 / 7) { var availablePowerups = []; if (!powerupActive.doubleBarrel) availablePowerups.push('doubleBarrel'); if (!powerupActive.slowTime) availablePowerups.push('slowTime'); if (!powerupActive.autoAim) availablePowerups.push('autoAim'); if (availablePowerups.length > 0) { var which = availablePowerups[Math.floor(Math.random() * availablePowerups.length)]; spawnPowerupCollectible(which); } } } spawnDucks(); // Spawn first batch immediately if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && ducksPerLevel > ducksAtOnce) { duckSpawnTimer = LK.setInterval(spawnDucks, duckSpawnInterval); } else if (gameMode === GAME_MODE_TIMEATTACK || gameMode === GAME_MODE_ENDLESS) { duckSpawnTimer = LK.setInterval(spawnDucks, duckSpawnInterval); } // Start timer if (timerInterval) LK.clearInterval(timerInterval); timerInterval = LK.setInterval(function () { if (!gameActive) return; if (gameMode === GAME_MODE_TIMEATTACK) { timeLeft--; timerText.setText('Time: ' + timeLeft); if (timeLeft <= 0) { endWave(); } } else if (gameMode === GAME_MODE_CLASSIC || !gameMode) { timeLeft--; timerText.setText('Time: ' + timeLeft); if (timeLeft <= 0) { endWave(); } } else if (gameMode === GAME_MODE_ENDLESS) { // No timer, but show "∞" or running time timerText.setText('Endless'); } }, 1000); } // End wave: check results, show dog if needed, advance or game over function endWave() { if (!waveActive) return; waveActive = false; gameActive = false; if (timerInterval) LK.clearInterval(timerInterval); // Remove remaining ducks for (var i = 0; i < ducks.length; i++) { if (ducks[i].parent) ducks[i].parent.removeChild(ducks[i]); } ducks = []; // --- Classic Mode --- if (gameMode === GAME_MODE_CLASSIC || !gameMode) { // Show dog if not enough ducks hit if (ducksHit < ducksToHit) { dog.laugh(2048 / 2, 2732 - 100); LK.effects.flashScreen(0xff0000, 800); // Show final score on Game Over LK.setTimeout(function () { var finalScoreText = new Text2('Final Score: ' + score, { size: 120, fill: 0xFFFF00 }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.x = 2048 / 2; finalScoreText.y = 2732 / 2; game.addChild(finalScoreText); LK.setTimeout(function () { if (finalScoreText.parent) finalScoreText.parent.removeChild(finalScoreText); LK.showGameOver(); }, 900); }, 400); // Game over after laugh waveResultTimeout = LK.setTimeout(function () { // handled above }, 1300); } else { // Advance to next level after short pause LK.effects.flashObject(levelText, 0x00ff00, 700); LK.setTimeout(function () { var finalScoreText = new Text2('Score: ' + score, { size: 120, fill: 0x00FF00 }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.x = 2048 / 2; finalScoreText.y = 2732 / 2; game.addChild(finalScoreText); LK.setTimeout(function () { if (finalScoreText.parent) finalScoreText.parent.removeChild(finalScoreText); level++; startLevel(); }, 700); }, 200); } } // --- Time Attack Mode --- else if (gameMode === GAME_MODE_TIMEATTACK) { // Show final score and return to menu LK.effects.flashScreen(0x00ffcc, 800); LK.setTimeout(function () { var finalScoreText = new Text2('Ducks Hit: ' + ducksHit, { size: 120, fill: 0x00FFCC }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.x = 2048 / 2; finalScoreText.y = 2732 / 2; game.addChild(finalScoreText); LK.setTimeout(function () { if (finalScoreText.parent) finalScoreText.parent.removeChild(finalScoreText); showMainMenu(); }, 1400); }, 400); } // --- Endless Mode --- else if (gameMode === GAME_MODE_ENDLESS) { endlessGameOver = true; LK.effects.flashScreen(0xff2222, 800); LK.setTimeout(function () { var finalScoreText = new Text2('Ducks Hit: ' + ducksHit, { size: 120, fill: 0xFF2222 }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.x = 2048 / 2; finalScoreText.y = 2732 / 2; game.addChild(finalScoreText); LK.setTimeout(function () { if (finalScoreText.parent) finalScoreText.parent.removeChild(finalScoreText); showMainMenu(); }, 1400); }, 400); } } // Handle tap/click to shoot game.down = function (x, y, obj) { if (!gameActive || !waveActive) return; // --- Power-up collectible check --- for (var i = powerupCollectibles.length - 1; i >= 0; i--) { var p = powerupCollectibles[i]; if (p.isHit(x, y)) { activatePowerup(p.type); if (p.parent) p.parent.removeChild(p); powerupCollectibles.splice(i, 1); LK.effects.flashObject(p, 0xffffff, 300); LK.getSound('hit').play(); return; } } // --- Bullets check --- if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && bulletsLeft <= 0) { LK.getSound('miss').play(); LK.effects.flashScreen(0x888888, 200); // Show reload animation (flash bullet icons red) for (var i = 0; i < bulletIcons.length; i++) { LK.effects.flashObject(bulletIcons[i], 0xff2222, 300); } // Optionally play reload sound if available // LK.getSound('reload').play(); return; } // Only decrement bullets and allow shooting if bulletsLeft > 0 (classic) if (gameMode === GAME_MODE_CLASSIC || !gameMode) { bulletsLeft--; updateBulletIcons(); if (bulletCountText && bulletCountText.setText) { bulletCountText.setText("Bullets: " + bulletsLeft); } // If bullets reach zero after this shot, trigger game over immediately if (bulletsLeft === 0) { LK.setTimeout(function () { LK.showGameOver(); }, 400); // short delay to allow last shot/crosshair to animate return; } } LK.getSound('shoot').play(); // Show crosshair at tap crosshair.x = x; crosshair.y = y; crosshair.visible = true; tween(crosshair, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { crosshair.visible = false; crosshair.alpha = 1; } }); // --- Power-up shooting logic --- // Auto-Aim: instantly hit nearest duck if (powerupActive.autoAim) { var nearestDuck = null; var minDist = 99999; for (var i = 0; i < ducks.length; i++) { var duck = ducks[i]; if (!duck.alive || duck.hit) continue; var dx = duck.x - x; var dy = duck.y - y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist) { minDist = dist; nearestDuck = duck; } } if (nearestDuck) { // Hit the duck nearestDuck.hit = true; nearestDuck.fall(function () { if (nearestDuck.parent) nearestDuck.parent.removeChild(nearestDuck); if (dog) { dog.show(nearestDuck.x, 2732 - 100); LK.getSound('dog_laugh').play(); LK.setTimeout(function () { dog.hide(); }, 2000); } }); ducksHit++; var duckScore = 10; var achievementMsg = ''; var achievementColor = 0xFFD700; if (typeof nearestDuck.type !== "undefined" && nearestDuck.type === 'golden') { duckScore = 50; achievementMsg = 'Golden Duck! +50'; achievementColor = 0xFFD700; LK.effects.flashObject(nearestDuck, 0xffe066, 400); } else { LK.effects.flashObject(nearestDuck, 0xffff00, 300); } // Headshot bonus (auto-aim never headshots) score += duckScore; LK.getSound('hit').play(); if (typeof achievementPopup !== "undefined" && achievementMsg) { achievementPopup.show(achievementMsg, achievementColor); } if (ducksHit === 3 && typeof achievementPopup !== "undefined") { achievementPopup.show('Triple Hit!', 0x00FFCC); } if (ducksHit === ducksToHit && typeof achievementPopup !== "undefined") { achievementPopup.show('Sharp Shooter!', 0x00FF00); } if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } scoreText.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } if (gameMode === GAME_MODE_ENDLESS) { endlessMissStreak = 0; } // End wave check var aliveDucks = 0; for (var i = 0; i < ducks.length; i++) { if (ducks[i].alive) aliveDucks++; } if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && (aliveDucks === 0 && bulletsLeft === 0 || aliveDucks === 0 && typeof duckSpawnTimer !== "undefined" && (!duckSpawnTimer || ducks.length === 0))) { endWave(); } return; } } // Double Barrel: shoot a wide spread, hit up to 2 ducks if close if (powerupActive.doubleBarrel) { var hitDucks = []; for (var i = 0; i < ducks.length; i++) { var duck = ducks[i]; if (!duck.alive || duck.hit) continue; var dx = duck.x - x; var dy = duck.y - y; var dist = Math.sqrt(dx * dx + dy * dy); var hitRadius = typeof duck.getHitboxRadius === "function" ? duck.getHitboxRadius() * 1.5 : 120; if (dist < hitRadius) { hitDucks.push({ duck: duck, dist: dist }); } } // Sort by distance, hit up to 2 closest hitDucks.sort(function (a, b) { return a.dist - b.dist; }); hitDucks = hitDucks.slice(0, 2); if (hitDucks.length > 0) { for (var h = 0; h < hitDucks.length; h++) { var hitDuck = hitDucks[h].duck; hitDuck.hit = true; hitDuck.fall(function () { if (hitDuck.parent) hitDuck.parent.removeChild(hitDuck); if (dog) { dog.show(hitDuck.x, 2732 - 100); LK.getSound('dog_laugh').play(); LK.setTimeout(function () { dog.hide(); }, 2000); } }); ducksHit++; var duckScore = 10; var achievementMsg = ''; var achievementColor = 0xFFD700; if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'golden') { duckScore = 50; achievementMsg = 'Golden Duck! +50'; achievementColor = 0xFFD700; LK.effects.flashObject(hitDuck, 0xffe066, 400); } else { LK.effects.flashObject(hitDuck, 0xffff00, 300); } // Headshot bonus var isHeadshot = typeof hitDuck.isHeadshot === "function" && hitDuck.isHeadshot(x, y); if (isHeadshot) { duckScore += 5; achievementMsg = 'Headshot! +' + duckScore; achievementColor = 0xFF2222; LK.effects.flashObject(hitDuck, 0xff2222, 400); } score += duckScore; LK.getSound('hit').play(); if (typeof achievementPopup !== "undefined" && achievementMsg) { achievementPopup.show(achievementMsg, achievementColor); } if (ducksHit === 3 && typeof achievementPopup !== "undefined") { achievementPopup.show('Triple Hit!', 0x00FFCC); } if (ducksHit === ducksToHit && typeof achievementPopup !== "undefined") { achievementPopup.show('Sharp Shooter!', 0x00FF00); } if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } scoreText.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } if (gameMode === GAME_MODE_ENDLESS) { endlessMissStreak = 0; } } // End wave check var aliveDucks = 0; for (var i = 0; i < ducks.length; i++) { if (ducks[i].alive) aliveDucks++; } if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && (aliveDucks === 0 && bulletsLeft === 0 || aliveDucks === 0 && typeof duckSpawnTimer !== "undefined" && (!duckSpawnTimer || ducks.length === 0))) { endWave(); } return; } } // --- Normal shooting logic --- // Check if a duck is hit (closest duck under tap, not already hit) var hitDuck = null; var minDist = 99999; for (var i = 0; i < ducks.length; i++) { var duck = ducks[i]; // Only allow hitting ducks that are alive and not hit yet if (!duck.alive || duck.hit) continue; var dx = duck.x - x; var dy = duck.y - y; var hitRadius = typeof duck.getHitboxRadius === "function" ? duck.getHitboxRadius() : 80; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < hitRadius && dist < minDist) { minDist = dist; hitDuck = duck; } } if (hitDuck) { // Armored duck: needs 2 hits if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'armored') { hitDuck.armorHit = (hitDuck.armorHit || 0) + 1; if (hitDuck.armorHit < hitDuck.hitPoints) { // Flash red, do not fall yet hitDuck.flashArmor(); LK.getSound('hit').play(); // Achievement for first hit if (typeof achievementPopup !== "undefined") { achievementPopup.show('Armored Duck! Hit again!', 0xff2222); } return; } } // Mark duck as hit and start fall animation hitDuck.hit = true; // Mark as hit immediately so it can't be hit again hitDuck.fall(function () { // When duck lands, remove from game and show dog retriever if (hitDuck.parent) hitDuck.parent.removeChild(hitDuck); // Show dog with duck at the bottom, after duck lands if (dog) { dog.show(hitDuck.x, 2732 - 100); LK.getSound('dog_laugh').play(); // Hide dog after 2 seconds LK.setTimeout(function () { dog.hide(); }, 2000); } }); ducksHit++; // Score system: +10 per duck hit, +50 for golden, +25 for mini, +5 for headshot, +20 for armored, +15 for fast if (typeof score === "undefined") score = 0; var duckScore = typeof hitDuck.points === "number" ? hitDuck.points : 10; var achievementMsg = ''; var achievementColor = 0xFFD700; if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'golden') { duckScore = 50; achievementMsg = 'Golden Duck! +50'; achievementColor = 0xFFD700; LK.effects.flashObject(hitDuck, 0xffe066, 400); } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'mini') { duckScore = 25; achievementMsg = 'Mini Duck! +25'; achievementColor = 0x00ccff; LK.effects.flashObject(hitDuck, 0x00ccff, 350); } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'armored') { duckScore = 20; achievementMsg = 'Armored Duck! +20'; achievementColor = 0xff2222; LK.effects.flashObject(hitDuck, 0xff2222, 400); } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'fast') { duckScore = 15; achievementMsg = 'Fast Duck! +15'; achievementColor = 0x00ffcc; LK.effects.flashObject(hitDuck, 0x00ffcc, 350); } else { LK.effects.flashObject(hitDuck, 0xffff00, 300); } // Headshot bonus var isHeadshot = typeof hitDuck.isHeadshot === "function" && hitDuck.isHeadshot(x, y); if (isHeadshot) { duckScore += 5; achievementMsg = 'Headshot! +' + duckScore; achievementColor = 0xFF2222; LK.effects.flashObject(hitDuck, 0xff2222, 400); } score += duckScore; LK.getSound('hit').play(); // Show achievement popup if any if (typeof achievementPopup !== "undefined" && achievementMsg) { achievementPopup.show(achievementMsg, achievementColor); } // Triple hit achievement (3 ducks in 1 level) if (ducksHit === 3 && typeof achievementPopup !== "undefined") { achievementPopup.show('Triple Hit!', 0x00FFCC); } // Sharp shooter (all ducks hit) if (ducksHit === ducksToHit && typeof achievementPopup !== "undefined") { achievementPopup.show('Sharp Shooter!', 0x00FF00); } // Update high score if needed if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } scoreText.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } // Endless mode: reset miss streak if (gameMode === GAME_MODE_ENDLESS) { endlessMissStreak = 0; } } else { // Miss: do nothing to ducks, just play miss sound and flash LK.getSound('miss').play(); LK.effects.flashScreen(0x888888, 120); // Track missed shots and show warning after 20 misses missedShots++; if (missedShots >= 20 && !missedWarningShown) { missedWarningShown = true; if (typeof achievementPopup !== "undefined") { achievementPopup.show("Careful! 20 missed shots!", 0xFF2222); } } // Endless mode: increment miss streak, end game if 3 in a row if (gameMode === GAME_MODE_ENDLESS) { endlessMissStreak++; if (endlessMissStreak >= 3) { endWave(); return; } } } // If all ducks are gone or out of bullets, end wave (classic only) var aliveDucks = 0; for (var i = 0; i < ducks.length; i++) { if (ducks[i].alive) aliveDucks++; } // Only end wave if all ducks are gone AND no more ducks will spawn, or if out of bullets if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && (aliveDucks === 0 && bulletsLeft === 0 || aliveDucks === 0 && typeof duckSpawnTimer !== "undefined" && (!duckSpawnTimer || ducks.length === 0))) { endWave(); } }; // Move handler for crosshair (optional, for desktop) game.move = function (x, y, obj) { // Optionally show crosshair following finger/mouse // crosshair.x = x; // crosshair.y = y; }; // Main update loop game.update = function () { if (!gameActive || !waveActive) return; // Update ducks for (var i = ducks.length - 1; i >= 0; i--) { var duck = ducks[i]; duck.update(); if (duck.escaped && !duck.hit) { ducksEscaped++; if (duck.parent) duck.parent.removeChild(duck); ducks.splice(i, 1); // Endless mode: treat escaped duck as a miss if (gameMode === GAME_MODE_ENDLESS) { endlessMissStreak++; if (endlessMissStreak >= 3) { endWave(); return; } } } } // --- Power-up collectibles update --- for (var i = powerupCollectibles.length - 1; i >= 0; i--) { powerupCollectibles[i].update(); } // --- Power-up timers/icons update --- for (var ptype in powerupActive) { if (powerupActive[ptype] && powerupTimers[ptype] > 0) { // Decrement timer (per second, but update is 60fps) if (LK.ticks % 60 === 0) { powerupTimers[ptype]--; if (powerupIcons[ptype] && powerupIcons[ptype]._timerTxt) { powerupIcons[ptype]._timerTxt.setText(powerupTimers[ptype] + "s"); } } } } // If all ducks gone, end wave (classic only) var aliveDucks = 0; for (var i = 0; i < ducks.length; i++) { if (ducks[i].alive) aliveDucks++; } if ((gameMode === GAME_MODE_CLASSIC || !gameMode) && aliveDucks === 0 && typeof duckSpawnTimer !== "undefined" && (!duckSpawnTimer || ducks.length === 0)) { endWave(); } }; // On game over, reset everything LK.on('gameover', function () { if (timerInterval) LK.clearInterval(timerInterval); if (waveResultTimeout) LK.clearTimeout(waveResultTimeout); if (typeof duckSpawnTimer !== "undefined" && duckSpawnTimer) { LK.clearInterval(duckSpawnTimer); duckSpawnTimer = null; } gameActive = false; waveActive = false; level = 1; ducks = []; bullets = []; ducksHit = 0; ducksEscaped = 0; // Reset bullets to 10 on gameover for level 1 bulletsLeft = 10; score = 0; updateBulletIcons(); levelText.setText('Level 1'); scoreText.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; if (typeof highScoreText !== "undefined") { highScoreText.setText('High: ' + highScore); } } timerText.setText('Time: 15'); if (dog) dog.hide(); // Show main menu after short delay LK.setTimeout(function () { showMainMenu(); }, 1200); }); // On you win (if you want to add a win condition, e.g. after level 10) LK.on('youwin', function () { if (timerInterval) LK.clearInterval(timerInterval); if (waveResultTimeout) LK.clearTimeout(waveResultTimeout); if (typeof duckSpawnTimer !== "undefined" && duckSpawnTimer) { LK.clearInterval(duckSpawnTimer); duckSpawnTimer = null; } gameActive = false; waveActive = false; // Show win, then reset LK.setTimeout(function () { level = 1; ducks = []; bullets = []; ducksHit = 0; ducksEscaped = 0; bulletsLeft = 10; score = 0; updateBulletIcons(); levelText.setText('Level 1'); scoreText.setText('Score: ' + score); if (score > highScore) { highScore = score; storage.highScore = highScore; } timerText.setText('Time: 15'); if (dog) dog.hide(); showMainMenu(); }, 1800); }); // Start music LK.playMusic('bgm', { fade: { start: 0, end: 0.5, duration: 1200 } }); // Start first level // (Removed: now handled by showMainMenu)
===================================================================
--- original.js
+++ change.js
@@ -104,72 +104,122 @@
});
// Duck class
var Duck = Container.expand(function () {
var self = Container.call(this);
- // Duck type: normal, fast, golden
- self.type = 'normal';
+ // Duck type: normal, fast, golden, armored, mini
+ // Type selection is handled by spawn logic, but fallback to random if not set
+ self.type = self.type || 'normal';
+ // Duck asset selection
+ var duckAsset = null;
var duckColors = ['duck_green', 'duck_blue', 'duck_red'];
- // 1 in 12 chance for golden duck
- if (Math.random() < 1 / 12) {
- self.type = 'golden';
- var duckAsset = self.attachAsset('duck_red', {
+ // If type is not set, pick by probability (for backward compatibility)
+ if (!self.type || self.type === 'normal' || self.type === 'fast') {
+ // 1 in 12 chance for golden duck
+ if (Math.random() < 1 / 12) {
+ self.type = 'golden';
+ } else if (Math.random() < 1 / 10) {
+ self.type = 'armored';
+ } else if (Math.random() < 1 / 7) {
+ self.type = 'mini';
+ } else if (Math.random() < 0.25) {
+ self.type = 'fast';
+ } else {
+ self.type = 'normal';
+ }
+ }
+ // Asset and property assignment by type
+ if (self.type === 'golden') {
+ duckAsset = self.attachAsset('duck_golden', {
anchorX: 0.5,
anchorY: 0.5
});
duckAsset.tint = 0xFFD700; // gold tint
+ self.speed = 10 + Math.random() * 2.5; // very fast
+ self.points = 50;
+ self.hitPoints = 1;
+ self.sizeScale = 1.0;
+ } else if (self.type === 'armored') {
+ duckAsset = self.attachAsset('duck_armored', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 4 + Math.random() * 2; // slower
+ self.points = 20;
+ self.hitPoints = 2;
+ self.sizeScale = 1.0;
+ self.armorHit = 0;
+ } else if (self.type === 'mini') {
+ duckAsset = self.attachAsset('duck_mini', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 8 + Math.random() * 2.5; // fast
+ self.points = 25;
+ self.hitPoints = 1;
+ self.sizeScale = 0.55;
+ } else if (self.type === 'fast') {
+ var colorIdx = Math.floor(Math.random() * duckColors.length);
+ duckAsset = self.attachAsset(duckColors[colorIdx], {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 7 + Math.random() * 2.5;
+ self.points = 15;
+ self.hitPoints = 1;
+ self.sizeScale = 1.0;
} else {
- // 1 in 4 chance for fast duck
- if (Math.random() < 0.25) self.type = 'fast';
+ // normal
var colorIdx = Math.floor(Math.random() * duckColors.length);
- var duckAsset = self.attachAsset(duckColors[colorIdx], {
+ duckAsset = self.attachAsset(duckColors[colorIdx], {
anchorX: 0.5,
anchorY: 0.5
});
+ self.speed = 5 + Math.random() * 2;
+ self.points = 10;
+ self.hitPoints = 1;
+ self.sizeScale = 1.0;
}
- // Set initial position and movement
- self.speed = 6 + Math.random() * 4; // Will be set by level
+ // Scale asset for mini duck
+ if (self.sizeScale !== 1.0) {
+ duckAsset.scaleX = duckAsset.scaleY = self.sizeScale;
+ }
self.angle = 0; // radians, will be set by level
self.alive = true;
self.hit = false;
self.escaped = false;
// For animation
self.flyTween = null;
- // --- Hitbox properties for all ducks (full sprite, slightly larger for fast/golden ducks) ---
+ // --- Hitbox properties for all ducks (full sprite, slightly larger for fast/golden/mini ducks) ---
self.getHitboxRadius = function () {
- var baseRadius = Math.max(duckAsset.width, duckAsset.height) * 0.5;
- if (self.type === 'fast' || self.type === 'golden' || self.speed >= 6) {
- return baseRadius * 0.7; // 40% larger for fast/golden
+ var baseRadius = Math.max(duckAsset.width, duckAsset.height) * 0.5 * (self.sizeScale || 1.0);
+ if (self.type === 'fast' || self.type === 'golden' || self.type === 'mini' || self.speed >= 7) {
+ return baseRadius * 0.7;
}
return baseRadius * 0.55;
};
// Head hitbox (top 1/3 of duck)
self.isHeadshot = function (x, y) {
- // Head is upper 1/3 of duck sprite
var dx = self.x - x;
var dy = self.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
- var headRadius = duckAsset.height * 0.18;
- // Head center is above the duck center
- var headCenterY = self.y - duckAsset.height * 0.22;
+ var headRadius = duckAsset.height * 0.18 * (self.sizeScale || 1.0);
+ var headCenterY = self.y - duckAsset.height * 0.22 * (self.sizeScale || 1.0);
var headDist = Math.sqrt((self.x - x) * (self.x - x) + (headCenterY - y) * (headCenterY - y));
return headDist < headRadius;
};
// Called every tick
self.update = function () {
- // Only move if alive and not hit
if (!self.alive || self.hit) return;
self.x += Math.cos(self.angle) * self.speed;
self.y += Math.sin(self.angle) * self.speed;
- // If out of bounds, mark as escaped
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.escaped = true;
self.alive = false;
}
};
// Animate duck falling when hit
self.fall = function (_onFinish) {
self.alive = false;
- // self.hit = true; // Already set on hit, do not set here to avoid blocking future hits on new ducks
tween(self, {
rotation: Math.PI * 1.5,
y: self.y + 400
}, {
@@ -179,8 +229,14 @@
if (_onFinish) _onFinish();
}
});
};
+ // For armored duck: flash red after first hit
+ self.flashArmor = function () {
+ if (duckAsset) {
+ LK.effects.flashObject(duckAsset, 0xff2222, 300);
+ }
+ };
return self;
});
// Power-up collectible class
var PowerupCollectible = Container.expand(function () {
@@ -238,8 +294,11 @@
/****
* Game Code
****/
+// Use red duck as base for mini
+// Use blue duck as base for armored
+// Use red duck as base for golden
// Power-up icons and retro overlay
// Music
// Sounds
// sky blue
@@ -758,9 +817,27 @@
return;
}
var ducksThisWave = gameMode === GAME_MODE_CLASSIC || !gameMode ? Math.min(ducksAtOnce, ducksPerLevel - ducksSpawned) : ducksAtOnce;
for (var d = 0; d < ducksThisWave; d++) {
+ // Determine duck type based on level and random chance
+ var duckType = 'normal';
+ var goldenChance = 0.04 + 0.01 * Math.min(level, 10); // 4% + 1% per level, max 14%
+ var armoredChance = 0.06 + 0.02 * Math.max(level - 2, 0); // 6% + 2% per level after 2
+ var miniChance = 0.08 + 0.02 * Math.max(level - 3, 0); // 8% + 2% per level after 3
+ var r = Math.random();
+ if (r < goldenChance) {
+ duckType = 'golden';
+ } else if (r < goldenChance + armoredChance) {
+ duckType = 'armored';
+ } else if (r < goldenChance + armoredChance + miniChance) {
+ duckType = 'mini';
+ } else if (level >= 2 && r < goldenChance + armoredChance + miniChance + 0.15) {
+ duckType = 'fast';
+ } else {
+ duckType = 'normal';
+ }
var duck = new Duck();
+ duck.type = duckType;
// Randomize start edge: 0=left, 1=right, 2=bottom
var edge = Math.floor(Math.random() * 3);
var startX, startY, angle;
if (edge === 0) {
@@ -781,9 +858,20 @@
}
duck.x = startX;
duck.y = startY;
duck.angle = angle;
- duck.speed = duckSpeedMin + Math.random() * (duckSpeedMax - duckSpeedMin);
+ // Duck speed is set in Duck class by type, but override for level scaling
+ if (duck.type === 'golden') {
+ duck.speed = Math.max(duck.speed, duckSpeedMax + 2);
+ } else if (duck.type === 'mini') {
+ duck.speed = Math.max(duck.speed, duckSpeedMax + 1);
+ } else if (duck.type === 'fast') {
+ duck.speed = Math.max(duck.speed, duckSpeedMax);
+ } else if (duck.type === 'armored') {
+ duck.speed = Math.min(duck.speed, duckSpeedMin + 1.5);
+ } else {
+ duck.speed = duckSpeedMin + Math.random() * (duckSpeedMax - duckSpeedMin);
+ }
ducks.push(duck);
// Always add ducks above background and below crosshair/dog/UI
game.addChild(duck);
// Move dog and crosshair to top of display list if present
@@ -1186,8 +1274,22 @@
hitDuck = duck;
}
}
if (hitDuck) {
+ // Armored duck: needs 2 hits
+ if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'armored') {
+ hitDuck.armorHit = (hitDuck.armorHit || 0) + 1;
+ if (hitDuck.armorHit < hitDuck.hitPoints) {
+ // Flash red, do not fall yet
+ hitDuck.flashArmor();
+ LK.getSound('hit').play();
+ // Achievement for first hit
+ if (typeof achievementPopup !== "undefined") {
+ achievementPopup.show('Armored Duck! Hit again!', 0xff2222);
+ }
+ return;
+ }
+ }
// Mark duck as hit and start fall animation
hitDuck.hit = true; // Mark as hit immediately so it can't be hit again
hitDuck.fall(function () {
// When duck lands, remove from game and show dog retriever
@@ -1202,18 +1304,33 @@
}, 2000);
}
});
ducksHit++;
- // Score system: +10 per duck hit, +50 for golden, +5 for headshot
+ // Score system: +10 per duck hit, +50 for golden, +25 for mini, +5 for headshot, +20 for armored, +15 for fast
if (typeof score === "undefined") score = 0;
- var duckScore = 10;
+ var duckScore = typeof hitDuck.points === "number" ? hitDuck.points : 10;
var achievementMsg = '';
var achievementColor = 0xFFD700;
if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'golden') {
duckScore = 50;
achievementMsg = 'Golden Duck! +50';
achievementColor = 0xFFD700;
LK.effects.flashObject(hitDuck, 0xffe066, 400);
+ } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'mini') {
+ duckScore = 25;
+ achievementMsg = 'Mini Duck! +25';
+ achievementColor = 0x00ccff;
+ LK.effects.flashObject(hitDuck, 0x00ccff, 350);
+ } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'armored') {
+ duckScore = 20;
+ achievementMsg = 'Armored Duck! +20';
+ achievementColor = 0xff2222;
+ LK.effects.flashObject(hitDuck, 0xff2222, 400);
+ } else if (typeof hitDuck.type !== "undefined" && hitDuck.type === 'fast') {
+ duckScore = 15;
+ achievementMsg = 'Fast Duck! +15';
+ achievementColor = 0x00ffcc;
+ LK.effects.flashObject(hitDuck, 0x00ffcc, 350);
} else {
LK.effects.flashObject(hitDuck, 0xffff00, 300);
}
// Headshot bonus
bullet. In-Game asset. 2d. High contrast. No shadows
crosshair. In-Game asset. 2d. High contrast. No shadows
pixart jungle. In-Game asset. 2d. High contrast. No shadows
pixart hunting brown dog with black ears and white mouth holding a gun. In-Game asset. 2d. High contrast. No shadows
pixart brown hunting dog with black ears and white mouth laughing. In-Game asset. 2d. High contrast. No shadows
pixart blue duck flying. In-Game asset. 2d. High contrast. No shadows
pixart green duck flying. In-Game asset. 2d. High contrast. No shadows
pixart red duck flying. In-Game asset. 2d. High contrast. No shadows
pigeon flying. In-Game asset. 2d. High contrast. No shadows
black wall. In-Game asset. 2d. High contrast. No shadows
empty beer glass. In-Game asset. 2d. High contrast. No shadows
pixart sand watch. In-Game asset. 2d. High contrast. No shadows
dalmation dog holding a gun. In-Game asset. 2d. High contrast. No shadows. hunting
labrador dog hold a hunt gun. In-Game asset. 2d. High contrast. No shadows
dalmatian dog laugh. In-Game asset. 2d. High contrast. No shadows
labrador laugh. In-Game asset. 2d. High contrast. No shadows