Code edit (1 edits merged)
Please save this source code
User prompt
during a boss wave do not start the next wave until both bosses have been defeated
User prompt
after a poker chip has been frozen add a 2 second immunity period before it can be frozen again
User prompt
update as needed with: // 1. Add freeze properties to PokerChip constructor (add after burn properties): self.freezeDuration = 0; self.originalSpeed = 0; self.iceCube = null; // Will hold the ice cube graphic // 2. Add freeze methods to PokerChip class (add after applyBurn method): self.applyFreeze = function(duration) { // Don't freeze if already frozen - just refresh duration if (self.freezeDuration > 0) { self.freezeDuration = Math.max(self.freezeDuration, duration); return; } self.freezeDuration = duration; // Store original speed and stop movement self.originalSpeed = self.speed; self.speed = 0; // Create ice cube encasement if (!self.iceCube) { self.iceCube = LK.getAsset('iceCube', { anchorX: 0.5, anchorY: 0.5 }); self.iceCube.scale.set(1.2); // Slightly larger than chip self.iceCube.alpha = 0.8; self.addChild(self.iceCube); } self.iceCube.visible = true; // Ice formation animation self.iceCube.scale.set(0.1); self.iceCube.alpha = 0; tween(self.iceCube, { scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }, { duration: 300, easing: tween.backOut }); }; self.processFreeze = function() { if (self.freezeDuration <= 0) return; self.freezeDuration--; // Create occasional ice sparkle effects if (Math.random() < 0.1) { createIceSparkles(self.x, self.y); } // When freeze expires, break the ice if (self.freezeDuration <= 0) { self.speed = self.originalSpeed; if (self.iceCube) { // Ice breaking animation tween(self.iceCube, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, easing: tween.quadOut, onFinish: function() { self.iceCube.visible = false; } }); // Create ice shatter particles createIceShatterEffect(self.x, self.y); } } }; // 3. In PokerChip activate method, add after burn reset: // NEW: Reset freeze status to ensure clean state self.freezeDuration = 0; self.originalSpeed = 0; if (self.iceCube) { self.iceCube.visible = false; } // 4. In PokerChip update method, add after processBurnDamage(): // NEW: Process freeze effect self.processFreeze(); // 5. In Bullet update method, add after burn effect check: // NEW: Check if this is a spades bullet and freeze mod is equipped if (self.suit === 'spades' && ModSystem.getEquippedModAsset('spades') === 'freezeSpadeMod') { // Calculate freeze duration (3 seconds base) var freezeDuration = 180; // 180 ticks at 60fps = 3 seconds // Apply freeze effect self.target.applyFreeze(freezeDuration); } // 6. Add these effect functions after createBurnEffect function: function createIceSparkles(x, y) { var numSparkles = 2; for (var i = 0; i < numSparkles; i++) { var sparkle = LK.getAsset('iceCube', { anchorX: 0.5, anchorY: 0.5 }); sparkle.x = x + (Math.random() - 0.5) * 60; sparkle.y = y + (Math.random() - 0.5) * 60; sparkle.scale.set(0.1 + Math.random() * 0.1); sparkle.alpha = 0.6; sparkle.tint = 0x88ddff; // Light blue tint gameLayer.addChild(sparkle); tween(sparkle, { y: sparkle.y - 20, alpha: 0, scaleX: 0.05, scaleY: 0.05 }, { duration: 800, easing: tween.quadOut, onFinish: function(p) { return function() { if (p.parent) { p.parent.removeChild(p); } }; }(sparkle) }); } } function createIceShatterEffect(x, y) { var numShards = 6; for (var i = 0; i < numShards; i++) { var shard = LK.getAsset('iceCube', { anchorX: 0.5, anchorY: 0.5 }); shard.x = x; shard.y = y; shard.scale.set(0.2 + Math.random() * 0.2); shard.tint = 0xaaeeff; // Ice blue tint var angle = (Math.PI * 2 * i) / numShards + (Math.random() - 0.5) * 0.5; var distance = 40 + Math.random() * 30; var targetX = x + Math.cos(angle) * distance; var targetY = y + Math.sin(angle) * distance; gameLayer.addChild(shard); tween(shard, { x: targetX, y: targetY, alpha: 0, rotation: Math.random() * Math.PI * 2, scaleX: 0.05, scaleY: 0.05 }, { duration: 600, easing: tween.quadOut, onFinish: function(p) { return function() { if (p.parent) { p.parent.removeChild(p); } }; }(shard) }); } } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make sure when playing a 5th card of the same value onto a row it doesnt nullify the existing 4 of a kind
User prompt
Jokers cannot be played onto the board, they can only be used to level up cards. remove support for their board presence
Code edit (1 edits merged)
Please save this source code
User prompt
update as needed with: self.fire = function () { var target = self.findTarget(); if (!target) { return; } // Check if this is a clubs card with spreadshot mod equipped var isSpreadshot = (self.cardData.suit === 'clubs' && ModSystem.getEquippedModAsset('clubs') === 'spreadClubMod'); if (isSpreadshot) { // Spreadshot: reduced damage but hits multiple targets var spreadDamage = Math.floor(self.damage * 0.7); // 30% damage reduction var maxTargets = Math.min(1 + self.level, 5); // 1 + level targets, max 5 // Find multiple targets var targets = self.isPlayerCard ? activePlayerChips : activeAIChips; var validTargets = []; // Get all valid targets and sort by distance for (var i = 0; i < targets.length; i++) { var chip = targets[i]; if (chip.active && chip.health > 0 && chip.visible) { var dx = chip.x - self.x; var dy = chip.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); validTargets.push({chip: chip, distance: distance}); } } // Sort by distance (closest first) validTargets.sort(function(a, b) { return a.distance - b.distance; }); // Fire at up to maxTargets var targetsHit = Math.min(maxTargets, validTargets.length); for (var i = 0; i < targetsHit; i++) { var bullet = PoolManager.getBullet(); if (bullet) { bullet.activate(self.x, self.y, validTargets[i].chip, spreadDamage, self.cardData.suit, self.isPlayerCard); gameLayer.addChild(bullet); activeBullets.push(bullet); } } } else { // Normal single-target firing var bullet = PoolManager.getBullet(); if (bullet) { bullet.activate(self.x, self.y, target, self.damage, self.cardData.suit, self.isPlayerCard); gameLayer.addChild(bullet); activeBullets.push(bullet); } } self.lastFired = LK.ticks; // Visual feedback for firing (enhanced for spreadshot) tween.stop(self, { scaleX: true, scaleY: true }); var scaleAmount = isSpreadshot ? 0.8 : 0.9; // More dramatic scale for spreadshot tween(self, { scaleX: scaleAmount, scaleY: scaleAmount }, { duration: 100, easing: tween.quadOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.elasticOut }); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
after every 10 levels, new cards dealt start one level higher
User prompt
update as needed with: update: function update() { this.waveTimer++; // Check if wave is complete if (this.waveTimer >= this.waveDuration) { // Check if both bosses were defeated in a boss wave if (this.isBossWave(this.waveNumber) && this.playerBossDefeated && this.aiBossDefeated) { console.log("Both bosses defeated! Moving to next round!"); createFloatingText('ROUND COMPLETE!', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0x00ff00); // Reset boss defeat flags this.playerBossDefeated = false; this.aiBossDefeated = false; } this.waveTimer = 0; this.waveNumber++; this.playerSpawnTimer = 0; this.aiSpawnTimer = 0; this.bossSpawned = false; // Reset boss spawn flag var waveType = this.isBossWave(this.waveNumber) ? "BOSS WAVE" : "Wave"; console.log(waveType + " " + this.waveNumber + " starting!"); return; } // BOSS WAVE LOGIC - Spawn bosses immediately at start of wave if (this.isBossWave(this.waveNumber)) { if (!this.bossSpawned && this.waveTimer === 1) { // Only on first tick of boss wave console.log("=== BOSS WAVE " + this.waveNumber + " STARTING ==="); // Spawn player boss var playerBossValue = this.getBossChipValue(this.waveNumber); ChipSpawner.spawnChip(playerBossValue, true); console.log("PLAYER BOSS spawned with value:", playerBossValue, "Active player chips:", activePlayerChips.length); // Spawn AI boss var aiBossValue = this.getBossChipValue(this.waveNumber); ChipSpawner.spawnChip(aiBossValue, false); console.log("AI BOSS spawned with value:", aiBossValue, "Active AI chips:", activeAIChips.length); this.bossSpawned = true; } // Boss defeat detection - only after bosses have had time to spawn if (this.bossSpawned && this.waveTimer > 120) { // 2 second delay console.log("Checking boss status - Player chips:", activePlayerChips.length, "AI chips:", activeAIChips.length); // Check if player side boss is defeated if (!this.playerBossDefeated && activePlayerChips.length === 0) { this.playerBossDefeated = true; console.log("Player defeated the boss!"); createFloatingText('BOSS DEFEATED!', SCREEN_WIDTH / 2, PLAYER_AREA_Y + SLOT_HEIGHT, 0xffd700); } // Check if AI side boss is defeated if (!this.aiBossDefeated && activeAIChips.length === 0) { this.aiBossDefeated = true; console.log("AI defeated the boss!"); createFloatingText('AI BOSS DEFEATED!', SCREEN_WIDTH / 2, AI_AREA_Y + SLOT_HEIGHT, 0xffd700); } // If both bosses defeated, immediately end the wave if (this.playerBossDefeated && this.aiBossDefeated) { this.waveTimer = this.waveDuration - 1; // Set to end on next tick } } return; // Don't do normal spawning during boss waves } // NORMAL WAVE LOGIC var currentSpawnInterval = this.spawnInterval; if (this.waveNumber === 1) { currentSpawnInterval = 90; // Slower spawning for wave 1 (1.5 seconds) } else if (this.waveNumber >= 2) { currentSpawnInterval = Math.max(45, this.spawnInterval - Math.floor((this.waveNumber - 2) * 2)); // Faster spawning for wave 2+ } // Spawn on player side this.playerSpawnTimer++; if (this.playerSpawnTimer >= currentSpawnInterval) { this.playerSpawnTimer = 0; this.spawnChip(true); } // Spawn on AI side this.aiSpawnTimer++; if (this.aiSpawnTimer >= currentSpawnInterval) { this.aiSpawnTimer = 0; this.spawnChip(false); } }
User prompt
update as needed with: function calculateGreedBonus(isPlayerSide, baseChipsEarned) { // Only calculate bonus if greed mod is equipped if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') { return 0; } var playArea = isPlayerSide ? gameState.playerPlayArea : gameState.aiPlayArea; var diamondCount = 0; for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = playArea[row][col]; if (card && card.cardData.suit === 'diamonds') { diamondCount++; } } } // 5% bonus per diamond card (caps at 50% for full diamond board) var bonusPercentage = diamondCount * 0.05; return Math.ceil(baseChipsEarned * bonusPercentage); } self.die = function () { var chipsEarned = Math.ceil(self.value * 1.5); if (self.isPlayerSide) { var greedBonus = calculateGreedBonus(true, chipsEarned); var totalEarned = chipsEarned + greedBonus; gameState.playerChips += totalEarned; if (greedBonus > 0) { createFloatingText('+' + formatNumberWithSuffix(totalEarned) + ' (+' + formatNumberWithSuffix(greedBonus) + ' greed)', self.x, self.y - 30, 0xffd700, 35); } } else { var greedBonus = calculateGreedBonus(false, chipsEarned); gameState.aiChips += chipsEarned + greedBonus; } PoolManager.returnChip(self); };
User prompt
update as needed with: // Alternative even more conservative approach: function calculateGreedBonus(isPlayerSide) { // Only calculate bonus if greed mod is equipped if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') { return 0; } var playArea = isPlayerSide ? gameState.playerPlayArea : gameState.aiPlayArea; var diamondCount = 0; var totalLevels = 0; // Count diamond cards and sum their levels for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = playArea[row][col]; if (card && card.cardData.suit === 'diamonds') { diamondCount++; totalLevels += card.level; } } } // Bonus = number of diamonds + total levels (much more conservative) return diamondCount + totalLevels; }
User prompt
update as needed with: update: function update() { this.waveTimer++; // Check if wave is complete if (this.waveTimer >= this.waveDuration) { // Check if both bosses were defeated in a boss wave if (this.isBossWave(this.waveNumber) && this.playerBossDefeated && this.aiBossDefeated) { console.log("Both bosses defeated! Moving to next round!"); createFloatingText('ROUND COMPLETE!', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0x00ff00); // Reset boss defeat flags this.playerBossDefeated = false; this.aiBossDefeated = false; } this.waveTimer = 0; this.waveNumber++; this.playerSpawnTimer = 0; this.aiSpawnTimer = 0; this.bossSpawned = false; // Reset boss spawn flag var waveType = this.isBossWave(this.waveNumber) ? "BOSS WAVE" : "Wave"; console.log(waveType + " " + this.waveNumber + " starting!"); return; } // Special handling for boss waves - spawn both bosses at the same time if (this.isBossWave(this.waveNumber)) { if (!this.bossSpawned) { // Spawn boss on both sides simultaneously this.spawnChip(true); // Player side this.spawnChip(false); // AI side this.bossSpawned = true; console.log("BOSS WAVE " + this.waveNumber + " - Bosses spawned on both sides!"); } // During boss wave, check if no enemies remain (boss defeated) // BUT only after giving the boss time to actually spawn and appear var bossSpawnDelay = 60; // 1 second delay (60 ticks at 60fps) if (this.waveTimer >= bossSpawnDelay) { // Check if player side boss is defeated if (!this.playerBossDefeated && activePlayerChips.length === 0) { this.playerBossDefeated = true; console.log("Player defeated the boss!"); createFloatingText('BOSS DEFEATED!', SCREEN_WIDTH / 2, PLAYER_AREA_Y + SLOT_HEIGHT, 0xffd700); } // Check if AI side boss is defeated if (!this.aiBossDefeated && activeAIChips.length === 0) { this.aiBossDefeated = true; console.log("AI defeated the boss!"); createFloatingText('AI BOSS DEFEATED!', SCREEN_WIDTH / 2, AI_AREA_Y + SLOT_HEIGHT, 0xffd700); } // If both bosses defeated, immediately end the wave if (this.playerBossDefeated && this.aiBossDefeated) { this.waveTimer = this.waveDuration - 1; // Set to end on next tick } } } else { // Normal wave spawning logic var currentSpawnInterval = this.spawnInterval; if (this.waveNumber === 1) { currentSpawnInterval = 90; // Slower spawning for wave 1 (1.5 seconds) } else if (this.waveNumber >= 2) { currentSpawnInterval = Math.max(45, this.spawnInterval - Math.floor((this.waveNumber - 2) * 2)); // Faster spawning for wave 2+ } // Spawn on player side this.playerSpawnTimer++; if (this.playerSpawnTimer >= currentSpawnInterval) { this.playerSpawnTimer = 0; this.spawnChip(true); } // Spawn on AI side this.aiSpawnTimer++; if (this.aiSpawnTimer >= currentSpawnInterval) { this.aiSpawnTimer = 0; this.spawnChip(false); } } }
User prompt
update as needed with: // Add this new function to calculate greed bonus function calculateGreedBonus(isPlayerSide) { // Only calculate bonus if greed mod is equipped if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') { return 0; } var playArea = isPlayerSide ? gameState.playerPlayArea : gameState.aiPlayArea; var totalBonus = 0; // Count diamond cards and their levels for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = playArea[row][col]; if (card && card.cardData.suit === 'diamonds') { // Base bonus of 2 chips per diamond card, multiplied by card level totalBonus += 2 * card.level; } } } return totalBonus; } // Modify the PokerChip die function to include greed bonus // Find this in the PokerChip class and replace it: self.die = function () { var chipsEarned = Math.ceil(self.value * 1.5); if (self.isPlayerSide) { // Add greed bonus for player var greedBonus = calculateGreedBonus(true); var totalEarned = chipsEarned + greedBonus; gameState.playerChips += totalEarned; // Show bonus text if greed mod provided extra chips if (greedBonus > 0) { createFloatingText('+' + formatNumberWithSuffix(totalEarned) + ' (+' + formatNumberWithSuffix(greedBonus) + ' greed)', self.x, self.y - 30, 0xffd700, 35); } } else { // Add greed bonus for AI var greedBonus = calculateGreedBonus(false); gameState.aiChips += chipsEarned + greedBonus; } PoolManager.returnChip(self); };
User prompt
clicking on the mods button should slide the overlay over, but currently it doesnt ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the titleselection overlay should slide over when the mods button is pressed and not just the icon ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add 50% more duration to burn effect
User prompt
add 50% more duration to burn effect ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
some chips are not having their tint reset when being returned to the pool. make sure all tints are cleared when returned to pool
User prompt
use burnheartmod asset for the burn particles ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
update as needed with: // 1. Remove tint from the applyBurn method in PokerChip self.applyBurn = function(damage, duration) { // Stack burn damage but refresh duration self.burnDamage += damage; self.burnDuration = Math.max(self.burnDuration, duration); self.burnTickTimer = 0; // REMOVED: Visual tint feedback - only use particles now // if (chipGraphics) { // chipGraphics.tint = 0xff4400; // Orange tint for burning // } // Create burn effect particles createBurnEffect(self.x, self.y); }; // 2. Update the processBurnDamage method to not manage tint self.processBurnDamage = function() { if (self.burnDuration <= 0) { // Ensure burn is completely cleared self.burnDamage = 0; self.burnTickTimer = 0; return; } self.burnTickTimer++; if (self.burnTickTimer >= self.burnTickInterval) { self.burnTickTimer = 0; self.burnDuration--; // Apply burn damage (10% of original hit damage) var actualBurnDamage = Math.ceil(self.burnDamage); self.health -= actualBurnDamage; self.updateHealthText(); // Create floating burn damage text createFloatingText('-' + actualBurnDamage, self.x + (Math.random() - 0.5) * 40, self.y - 30, 0xff4400, 30); // Burn particles createBurnEffect(self.x, self.y); if (self.health <= 0) { self.active = false; self.die(); return; } // Reduce burn damage over time (burn weakens) self.burnDamage *= 0.9; // Clear burn when duration expires - NO TINT MANAGEMENT if (self.burnDuration <= 0) { self.burnDamage = 0; self.burnTickTimer = 0; // REMOVED: No tint reset needed // if (chipGraphics && self.damageFlashTimer <= 0) { // chipGraphics.tint = 0xffffff; // } } } }; // 3. Update the chip update method to not interfere with burn tinting self.update = function () { if (!self.active) { return; } // Handle damage flash (keep this for regular damage) if (self.damageFlashTimer > 0) { self.damageFlashTimer--; if (chipGraphics) { // SIMPLIFIED: Only red flash for damage, no burn tint management chipGraphics.tint = self.damageFlashTimer > 0 ? 0xff0000 : 0xffffff; } } // Process burn damage self.processBurnDamage(); self.pathProgress += self.speed; var pathPos = PathSystem.getPositionAlongPath(self.pathProgress, self.isPlayerSide); if (pathPos.completed) { // Boss chips remove 2 hearts instead of 1 var heartsToRemove = self.isBoss ? 2 : 1; if (self.isPlayerSide) { gameState.playerLives -= heartsToRemove; } else { gameState.aiLives -= heartsToRemove; } PoolManager.returnChip(self); return; } self.x = pathPos.x; self.y = pathPos.y; }; // 4. Update createBurnEffect to make particles bigger and more visible function createBurnEffect(x, y) { var numParticles = 4; // Slightly more particles for (var i = 0; i < numParticles; i++) { var particle = LK.getAsset('explosionParticle', { anchorX: 0.5, anchorY: 0.5 }); particle.x = x + (Math.random() - 0.5) * 80; // Wider spread particle.y = y + (Math.random() - 0.5) * 80; particle.tint = Math.random() > 0.5 ? 0xff4400 : 0xff6600; // Orange/red flames particle.scale.set(0.6 + Math.random() * 0.8); // BIGGER: was 0.3 + Math.random() * 0.4 var angle = Math.random() * Math.PI * 2; var distance = Math.random() * 40 + 25; // Larger movement distance var duration = 400 + Math.random() * 500; // Slightly longer duration var targetX = x + Math.cos(angle) * distance; var targetY = y + Math.sin(angle) * distance - Math.random() * 50; // Float upward more gameLayer.addChild(particle); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.2, // Don't shrink as much scaleY: 0.2 }, { duration: duration, easing: tween.quadOut, onFinish: function(p) { return function() { if (p.parent) { p.parent.removeChild(p); } }; }(particle) }); } } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
update as needed with: // Fix the PoolManager.returnChip method to clear burn status var PoolManager = { // ... existing pool properties ... returnChip: function returnChip(chip) { // Force-set inactive state chip.active = false; chip.visible = false; chip.health = 0; // Ensure health is 0 // NEW: Clear burn status completely chip.burnDamage = 0; chip.burnDuration = 0; chip.burnTickTimer = 0; // Clear ownership flag var wasPlayerSide = chip.isPlayerSide; chip.isPlayerSide = null; // Remove from active arrays (check both arrays to be safe) var playerIndex = activePlayerChips.indexOf(chip); if (playerIndex !== -1) { activePlayerChips.splice(playerIndex, 1); } var aiIndex = activeAIChips.indexOf(chip); if (aiIndex !== -1) { activeAIChips.splice(aiIndex, 1); } // Remove from containers if (chip.parent) { chip.parent.removeChild(chip); } // Double-check removal from both containers if (activePlayerChipsContainer.children.indexOf(chip) !== -1) { activePlayerChipsContainer.removeChild(chip); } if (activeAIChipsContainer.children.indexOf(chip) !== -1) { activeAIChipsContainer.removeChild(chip); } }, // ... rest of existing PoolManager code ... }; // Also update the PokerChip activate method to ensure clean initialization // In the PokerChip class, modify the activate method: self.activate = function (value, isPlayerSide, startPos) { self.active = true; self.visible = true; self.value = value; self.isPlayerSide = isPlayerSide; // Mark boss chips (they have much higher values than normal chips) self.isBoss = value >= 100; // Health scales with chip value - more valuable chips are tankier self.maxHealth = value * 50; // Double health after every 10 waves var healthMultiplier = Math.pow(2, Math.floor(WaveSystem.waveNumber / 10)); self.maxHealth = self.maxHealth * healthMultiplier; self.health = self.maxHealth; self.pathProgress = 0; // NEW: Reset burn status to ensure clean state self.burnDamage = 0; self.burnDuration = 0; self.burnTickTimer = 0; // Set all chips to yellow chip speed self.speed = 0.03; // Reset damage flash timer self.damageFlashTimer = 0; // Recreate healthText to ensure proper styling if (healthText && healthText.parent) { healthText.parent.removeChild(healthText); } healthText = new Text2('', { size: 80, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 8 }); healthText.anchor.set(0.5, 0.5); self.addChild(healthText); self.setChipAppearance(); self.x = startPos.x; self.y = startPos.y; }; // Also update the processBurnDamage method to ensure proper cleanup self.processBurnDamage = function() { if (self.burnDuration <= 0) { // Ensure burn is completely cleared self.burnDamage = 0; self.burnTickTimer = 0; return; } self.burnTickTimer++; if (self.burnTickTimer >= self.burnTickInterval) { self.burnTickTimer = 0; self.burnDuration--; // Apply burn damage (10% of original hit damage) var actualBurnDamage = Math.ceil(self.burnDamage); self.health -= actualBurnDamage; self.updateHealthText(); // Create floating burn damage text createFloatingText('-' + actualBurnDamage, self.x + (Math.random() - 0.5) * 40, self.y - 30, 0xff4400, 30); // Burn particles createBurnEffect(self.x, self.y); if (self.health <= 0) { self.active = false; self.die(); return; } // Reduce burn damage over time (burn weakens) self.burnDamage *= 0.9; // Clear burn when duration expires if (self.burnDuration <= 0) { self.burnDamage = 0; self.burnTickTimer = 0; // Also reset tick timer if (chipGraphics && self.damageFlashTimer <= 0) { chipGraphics.tint = 0xffffff; // Reset tint } } } };
User prompt
still seeing poker chips spawning with burn status, need to clear status and clear timers when poker chips are defeated
User prompt
make sure the burn status is cleared on chips upon return to the pool
User prompt
update as needed with: // 1. Add burn status tracking to the PokerChip class constructor // Add these properties after the existing properties in PokerChip constructor: var PokerChip = Container.expand(function () { var self = Container.call(this); // ... existing properties ... self.active = false; self.health = 100; self.maxHealth = 100; self.value = 1; self.speed = 0.05; self.pathProgress = 0; self.isPlayerSide = true; self.damageFlashTimer = 0; // NEW: Burn status properties self.burnDamage = 0; self.burnDuration = 0; self.burnTickTimer = 0; self.burnTickInterval = 30; // Burn ticks every 0.5 seconds (30 ticks at 60fps) // ... existing chip graphics and health text setup ... // 2. Add burn application method to PokerChip self.applyBurn = function(damage, duration) { // Stack burn damage but refresh duration self.burnDamage += damage; self.burnDuration = Math.max(self.burnDuration, duration); self.burnTickTimer = 0; // Visual feedback for burn application if (chipGraphics) { chipGraphics.tint = 0xff4400; // Orange tint for burning } // Create burn effect particles createBurnEffect(self.x, self.y); }; // 3. Add burn tick method to PokerChip self.processBurnDamage = function() { if (self.burnDuration <= 0) { return; } self.burnTickTimer++; if (self.burnTickTimer >= self.burnTickInterval) { self.burnTickTimer = 0; self.burnDuration--; // Apply burn damage (10% of original hit damage) var actualBurnDamage = Math.ceil(self.burnDamage); self.health -= actualBurnDamage; self.updateHealthText(); // Create floating burn damage text createFloatingText('-' + actualBurnDamage, self.x + (Math.random() - 0.5) * 40, self.y - 30, 0xff4400, 30); // Burn particles createBurnEffect(self.x, self.y); if (self.health <= 0) { self.active = false; self.die(); return; } // Reduce burn damage over time (burn weakens) self.burnDamage *= 0.9; // Clear burn when duration expires if (self.burnDuration <= 0) { self.burnDamage = 0; if (chipGraphics && self.damageFlashTimer <= 0) { chipGraphics.tint = 0xffffff; // Reset tint } } } }; // ... existing methods ... // 4. Modify the existing PokerChip update method self.update = function () { if (!self.active) { return; } // Handle damage flash if (self.damageFlashTimer > 0) { self.damageFlashTimer--; if (chipGraphics) { chipGraphics.tint = self.damageFlashTimer > 0 ? 0xff0000 : (self.burnDuration > 0 ? 0xff4400 : 0xffffff); } } // NEW: Process burn damage self.processBurnDamage(); self.pathProgress += self.speed; var pathPos = PathSystem.getPositionAlongPath(self.pathProgress, self.isPlayerSide); if (pathPos.completed) { // Boss chips remove 2 hearts instead of 1 var heartsToRemove = self.isBoss ? 2 : 1; if (self.isPlayerSide) { gameState.playerLives -= heartsToRemove; } else { gameState.aiLives -= heartsToRemove; } PoolManager.returnChip(self); return; } self.x = pathPos.x; self.y = pathPos.y; }; return self; }); // 5. Modify bullet collision in the Bullet class update method // Find the existing collision detection in Bullet.update() and replace it with: // In the Bullet class, modify the collision detection part: var Bullet = Container.expand(function () { // ... existing bullet constructor code ... self.update = function () { if (!self.active) { return; } // ... existing movement and targeting code ... // MODIFY: The collision detection section if (self.target) { // ... existing movement calculation code ... var hitRadius = 85; var collisionDistance = distancePointToLine(self.target.x, self.target.y, prevX, prevY, newX, newY); if (collisionDistance <= hitRadius) { // Apply normal damage self.target.takeDamage(self.damage); // NEW: Check if this is a hearts bullet and flame mod is equipped if (self.suit === 'hearts' && ModSystem.getEquippedModAsset('hearts') === 'burnHeartMod') { // Calculate burn damage (10% of hit damage per tick) var burnDamage = self.damage * 0.1; var burnDuration = 5; // 5 ticks = ~2.5 seconds of burning // Apply burn effect self.target.applyBurn(burnDamage, burnDuration); } if (currentGraphic) { currentGraphic.visible = false; } PoolManager.returnBullet(self); } else { // No hit, move bullet forward self.x = newX; self.y = newY; } } else { // No target and not seeking, recycle if (currentGraphic) { currentGraphic.visible = false; } PoolManager.returnBullet(self); } }; return self; }); // 6. Add burn visual effects function // Add this function anywhere in the global scope: function createBurnEffect(x, y) { var numParticles = 3; for (var i = 0; i < numParticles; i++) { var particle = LK.getAsset('explosionParticle', { anchorX: 0.5, anchorY: 0.5 }); particle.x = x + (Math.random() - 0.5) * 60; particle.y = y + (Math.random() - 0.5) * 60; particle.tint = Math.random() > 0.5 ? 0xff4400 : 0xff6600; // Orange/red flames particle.scale.set(0.3 + Math.random() * 0.4); var angle = Math.random() * Math.PI * 2; var distance = Math.random() * 30 + 15; var duration = 300 + Math.random() * 400; var targetX = x + Math.cos(angle) * distance; var targetY = y + Math.sin(angle) * distance - Math.random() * 40; // Float upward gameLayer.addChild(particle); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: duration, easing: tween.quadOut, onFinish: function(p) { return function() { if (p.parent) { p.parent.removeChild(p); } }; }(particle) }); } } // 7. Update the mod description // Update the existing ModSystem.modData: var ModSystem = { // ... existing properties ... modData: { burnHeartMod: { suit: 'hearts', name: 'Flame', description: 'Hearts bullets apply burning damage over time. Burn damage is 10% of hit damage per tick for 2.5 seconds. Burn effects stack.' }, chipsDiamondMod: { suit: 'diamonds', name: 'Greed', description: 'Earn bonus chips when enemies are defeated, based on diamonds on board. Bonus scales with card level.' }, freezeSpadeMod: { suit: 'spades', name: 'Freeze', description: 'Spades have a chance to freeze enemies in place when dealing damage. Duration scales with card level.' }, spreadClubMod: { suit: 'clubs', name: 'Spreadshot', description: 'Clubs deal reduced damage but hit multiple enemies. Extra targets scale with card level.' } }, // ... rest of existing ModSystem code ... }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ /**** * Bullet Class ****/ var Bullet = Container.expand(function () { var self = Container.call(this); self.active = false; self.target = null; self.damage = 10; self.speed = 15.6; self.isPlayerCard = true; self.isSeekingLastPosition = false; self.targetLastX = 0; self.targetLastY = 0; self.suit = null; var currentGraphic = null; var suitGraphics = {}; // Initialize suit graphics ['hearts', 'diamonds', 'clubs', 'spades'].forEach(function (suit) { var graphic = self.attachAsset(ModSystem.getEquippedModAsset(suit) || suit + 'Suit', { anchorX: 0.5, anchorY: 0.5 }); graphic.scale.set(0.3); graphic.visible = false; suitGraphics[suit] = graphic; }); self.activate = function (startX, startY, target, damage, suit, isPlayerCard) { self.active = true; self.visible = true; self.x = startX; self.y = startY; self.target = target; self.damage = damage; self.isPlayerCard = isPlayerCard; self.suit = suit || 'hearts'; self.isSeekingLastPosition = false; if (self.target) { self.targetLastX = self.target.x; self.targetLastY = self.target.y; } if (currentGraphic) currentGraphic.visible = false; currentGraphic = suitGraphics[self.suit] || suitGraphics['hearts']; currentGraphic.visible = true; }; self.findNewTarget = function () { var targets = self.isPlayerCard ? activePlayerChips : activeAIChips; var bestTarget = null; var shortestDistance = Infinity; targets.forEach(function (chip) { if (chip.active) { var dx = chip.x - self.x; var dy = chip.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { shortestDistance = distance; bestTarget = chip; } } }); return bestTarget; }; self.update = function () { if (!self.active) return; if (self.target && !self.target.active) { var newTarget = self.findNewTarget(); if (newTarget) { self.target = newTarget; self.targetLastX = newTarget.x; self.targetLastY = newTarget.y; } else { self.isSeekingLastPosition = true; self.target = null; } } if (self.isSeekingLastPosition) { var dx = self.targetLastX - self.x; var dy = self.targetLastY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.isSeekingLastPosition = false; if (currentGraphic) currentGraphic.visible = false; PoolManager.returnBullet(self); } else { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } } else if (self.target) { var prevX = self.x; var prevY = self.y; self.targetLastX = self.target.x; self.targetLastY = self.target.y; var dx = self.target.x - self.x; var dy = self.target.y - self.y; var angle = Math.atan2(dy, dx); var newX = self.x + Math.cos(angle) * self.speed; var newY = self.y + Math.sin(angle) * self.speed; var hitRadius = 85; var collisionDistance = distancePointToLine(self.target.x, self.target.y, prevX, prevY, newX, newY); if (collisionDistance <= hitRadius) { self.target.takeDamage(self.damage); // Apply special effects based on mod if (self.suit === 'hearts' && ModSystem.getEquippedModAsset('hearts') === 'burnHeartMod') { var burnDamage = self.damage * 0.1; var burnDuration = 8; self.target.applyBurn(burnDamage, burnDuration); } if (self.suit === 'spades' && ModSystem.getEquippedModAsset('spades') === 'freezeSpadeMod') { self.target.applyFreeze(180); } if (currentGraphic) currentGraphic.visible = false; PoolManager.returnBullet(self); } else { self.x = newX; self.y = newY; } } else { if (currentGraphic) currentGraphic.visible = false; PoolManager.returnBullet(self); } }; return self; }); /**** * Card Class ****/ var Card = Container.expand(function (cardData) { var self = Container.call(this); self.cardData = cardData; self.level = 1; self.isInPlay = false; self.isPlayerCard = true; self.playSlotX = 0; self.playSlotY = 0; self.lastFired = 0; self.fireRate = 60; self.damage = 35; self.range = 200; self.handBonus = 1; // Outlines var createOutline = function createOutline(color, alpha) { var outline = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); outline.scale.set(1.1); outline.alpha = alpha; outline.tint = color; outline.visible = false; return outline; }; self.redOutline = createOutline(0xff0000, 1.0); self.greenOutline = createOutline(0x00ff00, 0.7); // Card graphics var cardGraphics = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); // Card value display if (cardData.suit !== 'joker') { var valueText = new Text2(cardData.value, { size: 56, fill: CardSystem.suitColors[cardData.suit] || 0x000000, weight: 800, stroke: 0x000000, strokeThickness: 0 }); valueText.anchor.set(0, 0); valueText.x = -95; valueText.y = -135; self.addChild(valueText); } // Suit graphics var suitAssetMap = { hearts: 'heartSuit', diamonds: 'diamondSuit', clubs: 'clubSuit', spades: 'spadeSuit' }; var suitAssetId = ModSystem.getEquippedModAsset(cardData.suit) || suitAssetMap[cardData.suit]; if (suitAssetId) { var suitGraphics = self.attachAsset(suitAssetId, { anchorX: 0.5, anchorY: 0.5 }); suitGraphics.y = -15; suitGraphics.scale.set(0.8); } else if (cardData.suit === 'joker') { var jokerGraphics = self.attachAsset('jokerSuit', { anchorX: 0.5, anchorY: 0.5 }); jokerGraphics.y = -15; jokerGraphics.scale.set(1.5); } // Level text var levelText = new Text2('Lvl 1', { size: 45, fill: 0x000000, weight: 800, stroke: 0x000000, strokeThickness: 0 }); levelText.anchor.set(0.5, 1); levelText.y = 128; self.addChild(levelText); self.activate = function (x, y, inPlay, isPlayerCard) { self.x = x; self.y = y; self.isInPlay = inPlay || false; self.isPlayerCard = isPlayerCard !== undefined ? isPlayerCard : true; self.visible = true; if (inPlay) self.calculateStats(); }; self.calculateStats = function () { var baseDamage = 10; var baseFireRate = 60; self.damage = Math.floor(baseDamage * Math.pow(1.6, self.level - 1) * self.handBonus); self.fireRate = Math.max(10, Math.floor(baseFireRate / (Math.pow(1.2, self.level - 1) * self.handBonus))); }; self.setLevel = function (newLevel) { if (self.cardData.suit === 'joker') { self.level = 1; levelText.visible = false; } else { self.level = newLevel; levelText.setText('Lvl ' + self.level); } self.calculateStats(); }; self.canMergeWith = function (otherCard) { if (!otherCard || otherCard === self || otherCard.cardData.suit === 'joker') return false; if (self.cardData.suit === 'joker') return true; var sameLevel = self.level === otherCard.level; var sameSuit = self.cardData.suit === otherCard.cardData.suit; var sameValue = self.cardData.value === otherCard.cardData.value; return sameLevel && (sameSuit || sameValue); }; self.mergeWith = function (otherCard) { if (!self.canMergeWith(otherCard)) return null; var newLevel = otherCard.level + 1; if (self.cardData.suit === 'joker') { var mergedCard = new Card(otherCard.cardData); mergedCard.setLevel(newLevel); return mergedCard; } var randomSuit = CardSystem.suits[Math.floor(Math.random() * CardSystem.suits.length)]; var randomValue = CardSystem.values[Math.floor(Math.random() * CardSystem.values.length)]; var newCardData = { suit: randomSuit, value: randomValue, id: randomSuit + '_' + randomValue }; var mergedCard = new Card(newCardData); mergedCard.setLevel(newLevel); return mergedCard; }; self.findTarget = function () { var targets = self.isPlayerCard ? activePlayerChips : activeAIChips; var bestTarget = null; var highestProgress = -1; targets.forEach(function (chip) { if (chip.active && chip.health > 0 && chip.visible && chip.pathProgress > highestProgress) { highestProgress = chip.pathProgress; bestTarget = chip; } }); return bestTarget; }; self.fire = function () { var target = self.findTarget(); if (!target) return; var isSpreadshot = self.cardData.suit === 'clubs' && ModSystem.getEquippedModAsset('clubs') === 'spreadClubMod'; if (isSpreadshot) { var spreadDamage = Math.floor(self.damage * 0.6); var maxTargets = Math.min(1 + self.level, 5); var targets = self.isPlayerCard ? activePlayerChips : activeAIChips; var validTargets = targets.filter(function (chip) { return chip.active && chip.health > 0 && chip.visible; }).map(function (chip) { return { chip: chip, distance: Math.sqrt(Math.pow(chip.x - self.x, 2) + Math.pow(chip.y - self.y, 2)) }; }).sort(function (a, b) { return a.distance - b.distance; }).slice(0, maxTargets); validTargets.forEach(function (_ref) { var chip = _ref.chip; var bullet = PoolManager.getBullet(); if (bullet) { bullet.activate(self.x, self.y, chip, spreadDamage, self.cardData.suit, self.isPlayerCard); gameLayer.addChild(bullet); activeBullets.push(bullet); } }); } else { var bullet = PoolManager.getBullet(); if (bullet) { bullet.activate(self.x, self.y, target, self.damage, self.cardData.suit, self.isPlayerCard); gameLayer.addChild(bullet); activeBullets.push(bullet); } } self.lastFired = LK.ticks; // Fire animation tween.stop(self, { scaleX: true, scaleY: true }); var scaleAmount = isSpreadshot ? 0.8 : 0.9; tween(self, { scaleX: scaleAmount, scaleY: scaleAmount }, Object.assign({}, ANIMATION_CONFIGS.cardFire, { onFinish: function onFinish() { return tween(self, { scaleX: 1, scaleY: 1 }, ANIMATION_CONFIGS.cardFireReturn); } })); }; self.update = function () { if (!self.isInPlay) return; if (LK.ticks - self.lastFired >= self.fireRate) { self.fire(); } }; self.setLevel(1); return self; }); /**** * Poker Chip Enemy Class ****/ var PokerChip = Container.expand(function () { var self = Container.call(this); self.active = false; self.health = 100; self.maxHealth = 100; self.value = 1; self.speed = 0.05; self.pathProgress = 0; self.isPlayerSide = true; self.damageFlashTimer = 0; self.isBoss = false; // Burn properties self.burnDamage = 0; self.burnDuration = 0; self.burnTickTimer = 0; self.burnTickInterval = 30; // Freeze properties self.freezeDuration = 0; self.originalSpeed = 0; self.iceCube = null; self.freezeImmunityTimer = 0; // Chip graphics var chipValues = [1, 5, 10, 25, 100]; var chipColors = ['yellowChip', 'redChip', 'greenChip', 'blueChip', 'purpleChip']; var chipGraphicsAssets = {}; var chipGraphics = null; chipValues.forEach(function (value, index) { var graphic = self.attachAsset(chipColors[index], { anchorX: 0.5, anchorY: 0.5 }); graphic.visible = false; chipGraphicsAssets[value] = graphic; }); var healthText = new Text2('', { size: 80, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 8 }); healthText.anchor.set(0.5, 0.5); self.addChild(healthText); self.applyBurn = function (damage, duration) { self.burnDamage += damage; self.burnDuration = Math.max(self.burnDuration, duration); self.burnTickTimer = 0; AnimationHelper.createParticleEffect(self.x, self.y, 'burnHeartMod', { count: 4, floatUp: 50 }); }; self.applyFreeze = function (duration) { if (self.freezeImmunityTimer > 0) return; if (self.freezeDuration > 0) { self.freezeDuration = Math.max(self.freezeDuration, duration); return; } self.freezeDuration = duration; self.originalSpeed = self.speed; self.speed = 0; if (!self.iceCube) { self.iceCube = LK.getAsset('iceCube', { anchorX: 0.5, anchorY: 0.5 }); self.iceCube.scale.set(1.2); self.iceCube.alpha = 0.8; self.addChild(self.iceCube); } self.iceCube.visible = true; self.iceCube.scale.set(0.1); self.iceCube.alpha = 0; tween(self.iceCube, { scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }, { duration: 300, easing: tween.backOut }); }; self.processFreeze = function () { if (self.freezeImmunityTimer > 0) self.freezeImmunityTimer--; if (self.freezeDuration <= 0) return; self.freezeDuration--; if (Math.random() < 0.1) { AnimationHelper.createParticleEffect(self.x, self.y, 'iceCube', { count: 2, scale: { min: 0.1, max: 0.2 }, alpha: 0.6, tint: 0x88ddff, floatUp: 20 }); } if (self.freezeDuration <= 0) { self.speed = self.originalSpeed; self.freezeImmunityTimer = 120; if (self.iceCube) { tween(self.iceCube, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, easing: tween.quadOut, onFinish: function onFinish() { return self.iceCube.visible = false; } }); AnimationHelper.createParticleEffect(self.x, self.y, 'iceCube', { count: 6, tint: 0xaaeeff, rotation: Math.PI * 2 }); } } }; self.processBurnDamage = function () { if (self.burnDuration <= 0) { self.burnDamage = 0; self.burnTickTimer = 0; return; } self.burnTickTimer++; if (self.burnTickTimer >= self.burnTickInterval) { self.burnTickTimer = 0; self.burnDuration--; var actualBurnDamage = Math.ceil(self.burnDamage); self.health -= actualBurnDamage; self.updateHealthText(); createFloatingText('-' + actualBurnDamage, self.x + (Math.random() - 0.5) * 40, self.y - 30, 0xff4400, 30); AnimationHelper.createParticleEffect(self.x, self.y, 'burnHeartMod', { count: 4, floatUp: 50 }); if (self.health <= 0) { self.active = false; self.die(); return; } self.burnDamage *= 0.9; if (self.burnDuration <= 0) { self.burnDamage = 0; self.burnTickTimer = 0; } } }; self.activate = function (value, isPlayerSide, startPos) { self.active = true; self.visible = true; self.value = value; self.isPlayerSide = isPlayerSide; self.isBoss = value >= 100; // Calculate health self.maxHealth = value * 50; var healthMultiplier = Math.pow(2, Math.floor(WaveSystem.waveNumber / 10)); self.maxHealth *= healthMultiplier; self.health = self.maxHealth; // Reset all states self.pathProgress = 0; self.burnDamage = 0; self.burnDuration = 0; self.burnTickTimer = 0; self.freezeDuration = 0; self.originalSpeed = 0; self.freezeImmunityTimer = 0; self.damageFlashTimer = 0; self.speed = 0.03; if (self.iceCube) self.iceCube.visible = false; self.setChipAppearance(); self.x = startPos.x; self.y = startPos.y; }; self.updateHealthText = function () { healthText.setText(formatNumberWithSuffix(Math.max(0, self.health))); }; self.setChipAppearance = function () { if (chipGraphics) chipGraphics.visible = false; chipGraphics = chipGraphicsAssets[self.value] || chipGraphicsAssets[1]; if (chipGraphics) chipGraphics.visible = true; self.updateHealthText(); }; self.takeDamage = function (damage) { self.health -= damage; self.updateHealthText(); self.damageFlashTimer = 10; if (self.health <= 0) { self.active = false; self.die(); } }; self.die = function () { var chipsEarned = Math.ceil(self.value * 1.5); if (self.isPlayerSide) { var greedBonus = calculateGreedBonus(true, chipsEarned); var totalEarned = chipsEarned + greedBonus; gameState.playerChips += totalEarned; if (greedBonus > 0) { createFloatingText("+".concat(formatNumberWithSuffix(totalEarned), " (+").concat(formatNumberWithSuffix(greedBonus), " greed)"), self.x, self.y - 30, 0xffd700, 35); } } else { var greedBonus = calculateGreedBonus(false, chipsEarned); gameState.aiChips += chipsEarned + greedBonus; } PoolManager.returnChip(self); }; self.update = function () { if (!self.active) return; // Handle damage flash if (self.damageFlashTimer > 0) { self.damageFlashTimer--; if (chipGraphics) { chipGraphics.tint = self.damageFlashTimer > 0 ? 0xff0000 : 0xffffff; } } self.processBurnDamage(); self.processFreeze(); if (!self.active) return; self.pathProgress += self.speed; var pathPos = PathSystem.getPositionAlongPath(self.pathProgress, self.isPlayerSide); if (pathPos.completed) { var heartsToRemove = self.isBoss ? 2 : 1; if (self.isPlayerSide) { gameState.playerLives -= heartsToRemove; } else { gameState.aiLives -= heartsToRemove; } PoolManager.returnChip(self); return; } self.x = pathPos.x; self.y = pathPos.y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0f3d0f }); /**** * Game Code ****/ /**** * Constants ****/ /**** * Card System ****/ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } var SCREEN_WIDTH = 2048; var SCREEN_HEIGHT = 2732; var PLAY_AREA_COLS = 5; var PLAY_AREA_ROWS = 2; var SLOT_WIDTH = 300; var SLOT_HEIGHT = 420; var DEAL_SLOT_WIDTH = 240; var DEAL_SLOT_HEIGHT = 330; var HAND_GAP = 30; var HAND_WIDTH = 5 * DEAL_SLOT_WIDTH + 4 * HAND_GAP; var HAND_START_X = (SCREEN_WIDTH - HAND_WIDTH) / 2; // Area positioning var AI_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2; var AI_AREA_Y = 150; var PLAYER_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2; var PLAYER_AREA_Y = SCREEN_HEIGHT - 1300; var PLAYER_DEAL_AREA_Y = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + 100; // Common animation configs var ANIMATION_CONFIGS = { cardFire: { duration: 100, easing: tween.quadOut }, cardFireReturn: { duration: 150, easing: tween.elasticOut }, cardMerge: { duration: 300, easing: tween.elasticOut }, cardDeal: { duration: 250, easing: tween.cubicOut }, cardSettle: { duration: 150, easing: tween.quadIn }, particleFade: { duration: 600, easing: tween.quadOut } }; /**** * Utility Functions ****/ function formatNumberWithSuffix(number) { var num = Math.round(number); var suffixes = [{ value: 1e12, suffix: 'T' }, { value: 1e9, suffix: 'B' }, { value: 1e6, suffix: 'M' }, { value: 1e3, suffix: 'K' }]; for (var _i = 0, _suffixes = suffixes; _i < _suffixes.length; _i++) { var _suffixes$_i = _suffixes[_i], value = _suffixes$_i.value, suffix = _suffixes$_i.suffix; if (num >= value) { return (num / value).toFixed(1).replace(/\.0$/, '') + suffix; } } return num.toString(); } function distancePointToLine(px, py, x1, y1, x2, y2) { var A = px - x1, B = py - y1; var C = x2 - x1, D = y2 - y1; var lenSq = C * C + D * D; if (lenSq === 0) return Math.sqrt(A * A + B * B); var param = Math.max(0, Math.min(1, (A * C + B * D) / lenSq)); var xx = x1 + param * C; var yy = y1 + param * D; return Math.sqrt((px - xx) * (px - xx) + (py - yy) * (py - yy)); } function getHandSlotPosition(index) { return { x: HAND_START_X + index * (DEAL_SLOT_WIDTH + HAND_GAP) + DEAL_SLOT_WIDTH / 2, y: PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2 }; } /**** * Animation Helpers ****/ var AnimationHelper = { createParticleEffect: function createParticleEffect(x, y, particleAsset, config) { var defaults = { count: 4, spread: 80, scale: { min: 0.3, max: 0.5 }, distance: { min: 25, max: 65 }, duration: { min: 400, max: 900 }, tint: 0xffffff, alpha: 1 }; var settings = Object.assign({}, defaults, config); for (var i = 0; i < settings.count; i++) { var particle = LK.getAsset(particleAsset, { anchorX: 0.5, anchorY: 0.5 }); particle.x = x + (Math.random() - 0.5) * settings.spread; particle.y = y + (Math.random() - 0.5) * settings.spread; particle.scale.set(settings.scale.min + Math.random() * (settings.scale.max - settings.scale.min)); particle.tint = settings.tint; particle.alpha = settings.alpha; var angle = Math.random() * Math.PI * 2; var distance = settings.distance.min + Math.random() * (settings.distance.max - settings.distance.min); var duration = settings.duration.min + Math.random() * (settings.duration.max - settings.duration.min); var targetX = x + Math.cos(angle) * distance; var targetY = y + Math.sin(angle) * distance - (settings.floatUp || 0); gameLayer.addChild(particle); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: settings.endScale || 0.1, scaleY: settings.endScale || 0.1, rotation: settings.rotation || 0 }, { duration: duration, easing: tween.quadOut, onFinish: function onFinish() { return particle.parent && particle.parent.removeChild(particle); } }); } } }; var CardSystem = { suits: ['hearts', 'diamonds', 'clubs', 'spades'], values: ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'], suitColors: { 'hearts': 0xff0000, 'diamonds': 0xff0000, 'clubs': 0x000000, 'spades': 0x000000 }, createDeck: function createDeck() { var _this = this; var deck = []; this.suits.forEach(function (suit) { _this.values.forEach(function (value) { deck.push({ suit: suit, value: value, id: "".concat(suit, "_").concat(value) }); }); }); deck.push({ suit: 'joker', value: 'red', id: 'joker_red' }); deck.push({ suit: 'joker', value: 'black', id: 'joker_black' }); return this.shuffleDeck(deck); }, shuffleDeck: function shuffleDeck(deck) { for (var i = deck.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var _ref2 = [deck[j], deck[i]]; deck[i] = _ref2[0]; deck[j] = _ref2[1]; } return deck; }, getCardValue: function getCardValue(card) { var valueMap = { 'A': 14, 'K': 13, 'Q': 12, 'J': 11 }; if (card.cardData.suit === 'joker') return 14; return valueMap[card.cardData.value] || parseInt(card.cardData.value); }, evaluatePokerHand: function evaluatePokerHand(cards) { var _this2 = this; if (!cards || cards.length === 0) { return { type: 'none', strength: 0, multiplier: 1, contributingCards: [] }; } var sortedCards = _toConsumableArray(cards).sort(function (a, b) { return _this2.getCardValue(b) - _this2.getCardValue(a); }); var values = sortedCards.map(function (card) { return _this2.getCardValue(card); }); var suits = sortedCards.map(function (card) { return card.cardData.suit; }); var valueCounts = {}; var suitCounts = {}; values.forEach(function (value) { return valueCounts[value] = (valueCounts[value] || 0) + 1; }); suits.forEach(function (suit) { return suitCounts[suit] = (suitCounts[suit] || 0) + 1; }); var counts = Object.values(valueCounts).sort(function (a, b) { return b - a; }); var isFlush = cards.length === 5 && Object.keys(suitCounts).length === 1; var isStraight = cards.length === 5 && this.checkStraight(values); // Check hand types in order of strength var handChecks = [{ check: function check() { return isFlush && isStraight && values[0] === 14 && values[4] === 10; }, result: { type: 'royal_flush', strength: 10, multiplier: 25, contributingCards: sortedCards } }, { check: function check() { return isFlush && isStraight; }, result: { type: 'straight_flush', strength: 9, multiplier: 12, contributingCards: sortedCards } }, { check: function check() { return counts[0] >= 4; }, result: function result() { var quadValue = parseInt(Object.keys(valueCounts).find(function (v) { return valueCounts[v] >= 4; })); return { type: 'four_of_a_kind', strength: 8, multiplier: 8, contributingCards: sortedCards.filter(function (c) { return _this2.getCardValue(c) === quadValue; }) }; } }, { check: function check() { return counts[0] === 3 && counts[1] === 2; }, result: { type: 'full_house', strength: 7, multiplier: 5, contributingCards: sortedCards } }, { check: function check() { return isFlush; }, result: { type: 'flush', strength: 6, multiplier: 3.5, contributingCards: sortedCards } }, { check: function check() { return isStraight; }, result: { type: 'straight', strength: 5, multiplier: 2.5, contributingCards: sortedCards } }, { check: function check() { return counts[0] === 3; }, result: function result() { var tripValue = parseInt(Object.keys(valueCounts).find(function (v) { return valueCounts[v] === 3; })); return { type: 'three_of_a_kind', strength: 4, multiplier: 2, contributingCards: sortedCards.filter(function (c) { return _this2.getCardValue(c) === tripValue; }) }; } }, { check: function check() { return counts[0] === 2 && counts[1] === 2; }, result: function result() { var pairValues = Object.keys(valueCounts).filter(function (v) { return valueCounts[v] === 2; }).map(function (v) { return parseInt(v); }); return { type: 'two_pair', strength: 3, multiplier: 1.5, contributingCards: sortedCards.filter(function (c) { return pairValues.includes(_this2.getCardValue(c)); }) }; } }, { check: function check() { return counts[0] === 2; }, result: function result() { var pairValue = parseInt(Object.keys(valueCounts).find(function (v) { return valueCounts[v] === 2; })); return { type: 'one_pair', strength: 2, multiplier: 1.2, contributingCards: sortedCards.filter(function (c) { return _this2.getCardValue(c) === pairValue; }) }; } }]; for (var _i2 = 0, _handChecks = handChecks; _i2 < _handChecks.length; _i2++) { var _handChecks$_i = _handChecks[_i2], check = _handChecks$_i.check, result = _handChecks$_i.result; if (check()) { return typeof result === 'function' ? result() : result; } } return { type: 'high_card', strength: 1, multiplier: 1, contributingCards: [sortedCards[0]] }; }, checkStraight: function checkStraight(values) { if (values.length !== 5) return false; // Check ace-low straight if (values[0] === 14 && values[1] === 5 && values[2] === 4 && values[3] === 3 && values[4] === 2) return true; // Check normal straight for (var i = 0; i < 4; i++) { if (values[i] - values[i + 1] !== 1) return false; } return true; } }; /**** * Object Pool Manager ****/ var PoolManager = { chipPool: [], bulletPool: [], CHIP_POOL_SIZE: 50, BULLET_POOL_SIZE: 100, init: function init() { for (var i = 0; i < this.CHIP_POOL_SIZE; i++) { var chip = new PokerChip(); chip.active = false; chip.visible = false; this.chipPool.push(chip); } for (var i = 0; i < this.BULLET_POOL_SIZE; i++) { var bullet = new Bullet(); bullet.active = false; bullet.visible = false; this.bulletPool.push(bullet); } }, getChip: function getChip() { return this.chipPool.find(function (chip) { return !chip.active; }) || null; }, getBullet: function getBullet() { return this.bulletPool.find(function (bullet) { return !bullet.active; }) || null; }, returnChip: function returnChip(chip) { chip.active = false; chip.visible = false; chip.health = 0; // Reset all visual components chip.children.forEach(function (child) { if (child.tint !== undefined) child.tint = 0xffffff; }); // Clear burn/freeze states chip.burnDamage = 0; chip.burnDuration = 0; chip.burnTickTimer = 0; chip.freezeDuration = 0; chip.freezeImmunityTimer = 0; // Remove from active arrays activePlayerChips = activePlayerChips.filter(function (c) { return c !== chip; }); activeAIChips = activeAIChips.filter(function (c) { return c !== chip; }); // Remove from containers if (chip.parent) chip.parent.removeChild(chip); }, returnBullet: function returnBullet(bullet) { var explosionColor = bullet.suit === 'hearts' || bullet.suit === 'diamonds' ? 0xff0000 : 0x333333; AnimationHelper.createParticleEffect(bullet.x, bullet.y, 'explosionParticle', { count: 5, tint: explosionColor, scale: { min: 0.5, max: 1 }, distance: { min: 20, max: 70 } }); bullet.active = false; bullet.visible = false; bullet.target = null; activeBullets = activeBullets.filter(function (b) { return b !== bullet; }); if (bullet.parent) bullet.parent.removeChild(bullet); } }; /**** * Path System ****/ var PathSystem = { playerPath: [], aiPath: [], playerPathLength: 0, aiPathLength: 0, init: function init() { var padding = 130; var verticalPadding = padding - 55; // Player path var leftX = PLAYER_AREA_X - padding + 10; var rightX = PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding - 30; var topY = PLAYER_AREA_Y - verticalPadding; var bottomY = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + verticalPadding; this.playerPath = [{ x: leftX, y: bottomY }, { x: leftX, y: topY }, { x: rightX, y: topY }, { x: rightX, y: bottomY }]; // AI path (upside down mirror) var aiLeftX = AI_AREA_X - padding; var aiRightX = AI_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding; var aiVerticalPadding = verticalPadding - 25; var aiTopY = AI_AREA_Y - aiVerticalPadding; var aiBottomY = AI_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + aiVerticalPadding; this.aiPath = [{ x: aiLeftX, y: aiTopY }, { x: aiLeftX, y: aiBottomY }, { x: aiRightX, y: aiBottomY }, { x: aiRightX, y: aiTopY }]; // Cache path lengths this.playerPathLength = this.calculatePathLength(this.playerPath); this.aiPathLength = this.calculatePathLength(this.aiPath); }, getPathStart: function getPathStart(isPlayerSide) { var path = isPlayerSide ? this.playerPath : this.aiPath; var baseStart = path[0]; var pathDirection = path[1]; var dx = pathDirection.x - baseStart.x; var dy = pathDirection.y - baseStart.y; var length = Math.sqrt(dx * dx + dy * dy); var normalizedX = dx / length; var normalizedY = dy / length; var pathOffset = Math.random() * 80; var sideOffset = (Math.random() - 0.5) * 40; return { x: baseStart.x - normalizedX * pathOffset + normalizedY * sideOffset, y: baseStart.y - normalizedY * pathOffset - normalizedX * sideOffset }; }, getPositionAlongPath: function getPositionAlongPath(progress, isPlayerSide) { var path = isPlayerSide ? this.playerPath : this.aiPath; var pathLength = isPlayerSide ? this.playerPathLength : this.aiPathLength; var targetDistance = progress / 100 * pathLength; if (targetDistance >= pathLength) { var lastPoint = path[path.length - 1]; return { x: lastPoint.x, y: lastPoint.y, completed: true }; } var currentDistance = 0; for (var i = 0; i < path.length - 1; i++) { var segmentLength = this.getDistance(path[i], path[i + 1]); if (currentDistance + segmentLength >= targetDistance) { var segmentProgress = (targetDistance - currentDistance) / segmentLength; return { x: path[i].x + (path[i + 1].x - path[i].x) * segmentProgress, y: path[i].y + (path[i + 1].y - path[i].y) * segmentProgress, completed: false }; } currentDistance += segmentLength; } var lastPoint = path[path.length - 1]; return { x: lastPoint.x, y: lastPoint.y, completed: true }; }, calculatePathLength: function calculatePathLength(path) { var total = 0; for (var i = 0; i < path.length - 1; i++) { total += this.getDistance(path[i], path[i + 1]); } return total; }, getDistance: function getDistance(p1, p2) { var dx = p2.x - p1.x; var dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } }; /**** * Chip Spawner ****/ var ChipSpawner = { spawnChip: function spawnChip(value, isPlayerSide) { var chip = PoolManager.getChip(); if (!chip) return; var activeChips = isPlayerSide ? activePlayerChips : activeAIChips; var minDistance = 85; var newPos = PathSystem.getPathStart(isPlayerSide); if (activeChips.length > 0) { var foundPosition = false; // Try random positions first for (var attempt = 0; attempt < 25; attempt++) { var candidatePos = PathSystem.getPathStart(isPlayerSide); var isValid = activeChips.every(function (otherChip) { var dx = candidatePos.x - otherChip.x; var dy = candidatePos.y - otherChip.y; return Math.sqrt(dx * dx + dy * dy) >= minDistance; }); if (isValid) { newPos = candidatePos; foundPosition = true; break; } } // Spiral search if random failed if (!foundPosition) { var baseStart = PathSystem.getPathStart(isPlayerSide); var searchRadius = minDistance; var angleStep = Math.PI / 6; spiral: for (var i = 0; i < 30; i++) { for (var angle = 0; angle < Math.PI * 2; angle += angleStep) { var testX = baseStart.x + Math.cos(angle) * searchRadius; var testY = baseStart.y + Math.sin(angle) * searchRadius; var isValid = activeChips.every(function (otherChip) { var dx = testX - otherChip.x; var dy = testY - otherChip.y; return Math.sqrt(dx * dx + dy * dy) >= minDistance; }); if (isValid) { newPos = { x: testX, y: testY }; foundPosition = true; break spiral; } } searchRadius += 20; } } } chip.activate(value, isPlayerSide, newPos); if (isPlayerSide) { activePlayerChips.push(chip); activePlayerChipsContainer.addChild(chip); } else { activeAIChips.push(chip); activeAIChipsContainer.addChild(chip); } } }; /**** * Mod System ****/ var ModSystem = { equippedMods: { hearts: null, diamonds: null, clubs: null, spades: null }, modData: { burnHeartMod: { suit: 'hearts', name: 'Flame', description: 'Hearts bullets apply burning damage over time. Burn damage is 10% of hit damage per tick for 4 seconds. Burn effects stack.' }, chipsDiamondMod: { suit: 'diamonds', name: 'Greed', description: 'Earn bonus chips when enemies are defeated, based on diamonds on board. Bonus scales with card level.' }, freezeSpadeMod: { suit: 'spades', name: 'Freeze', description: 'Spades have a chance to freeze enemies in place when dealing damage. Duration scales with card level.' }, spreadClubMod: { suit: 'clubs', name: 'Spreadshot', description: 'Clubs deal reduced damage but hit multiple enemies. Extra targets scale with card level.' } }, currentlyDisplayedMod: null, modDisplayContainer: null, topSuitGraphics: [], init: function init() { if (storage.equippedMods) { this.equippedMods = storage.equippedMods; } }, saveToStorage: function saveToStorage() { storage.equippedMods = this.equippedMods; }, equipMod: function equipMod(modAssetId) { var modData = this.modData[modAssetId]; if (!modData) return; this.equippedMods[modData.suit] = modAssetId; this.saveToStorage(); this.updateTopSuitDisplay(); this.hideModDisplay(); }, updateTopSuitDisplay: function updateTopSuitDisplay() { var _this3 = this; var suitAssets = ['heartSuit', 'diamondSuit', 'clubSuit', 'spadeSuit']; var suits = ['hearts', 'diamonds', 'clubs', 'spades']; this.topSuitGraphics.forEach(function (container, i) { if (!container) return; // Remove previous icon if (container.children.length > 1) { container.removeChildAt(1); } // Add new icon var suit = suits[i]; var assetId = _this3.equippedMods[suit] || suitAssets[i]; var suitIcon = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); suitIcon.scale.set(1.2); suitIcon.x = 0; suitIcon.y = 0; container.addChild(suitIcon); }); }, showModDisplay: function showModDisplay(modAssetId, sourceX, sourceY) { var _this4 = this; if (this.currentlyDisplayedMod) return; var modData = this.modData[modAssetId]; if (!modData) return; this.currentlyDisplayedMod = modAssetId; this.modDisplayContainer = new Container(); this.modDisplayContainer.interactive = true; uiLayer.addChild(this.modDisplayContainer); // Background blocker var bgBlocker = new Container(); bgBlocker.interactive = true; bgBlocker.hitArea = new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); this.modDisplayContainer.addChild(bgBlocker); // Mod display var enlargedMod = LK.getAsset(modAssetId, { anchorX: 0.5, anchorY: 0.5 }); enlargedMod.x = SCREEN_WIDTH / 2; enlargedMod.y = SCREEN_HEIGHT / 2 - 450; enlargedMod.scale.set(1); this.modDisplayContainer.addChild(enlargedMod); // Description overlay var overlayWidth = 600; var overlayHeight = 400; var descriptionOverlay = new Container(); var overlayBg = LK.getAsset('card', { anchorX: 0.5, anchorY: 0.5 }); overlayBg.scale.set(overlayWidth / 150, overlayHeight / 240); overlayBg.tint = 0x000000; overlayBg.alpha = 0.8; descriptionOverlay.addChild(overlayBg); var nameText = new Text2(modData.name, { size: 60, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 3 }); nameText.anchor.set(0.5, 0); nameText.y = -overlayHeight / 2 - 30; descriptionOverlay.addChild(nameText); var descText = new Text2(modData.description, { size: 50, fill: 0xffffff, weight: 600, stroke: 0x000000, strokeThickness: 2, align: 'center', wordWrap: true, wordWrapWidth: overlayWidth }); descText.anchor.set(0.5, 0.5); descText.y = 20; descriptionOverlay.addChild(descText); var equipButton = LK.getAsset('card', { anchorX: 0.5, anchorY: 0.5, interactive: true }); equipButton.scale.set(1.5, 0.5); equipButton.tint = 0x000000; equipButton.alpha = 0.8; equipButton.y = overlayHeight / 2 + 150; descriptionOverlay.addChild(equipButton); var equipText = new Text2('Equip', { size: 50, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 3 }); equipText.anchor.set(0.5, 0.5); equipText.y = equipButton.y; descriptionOverlay.addChild(equipText); descriptionOverlay.x = SCREEN_WIDTH / 2; descriptionOverlay.y = SCREEN_HEIGHT / 2 + 250; descriptionOverlay.alpha = 0; this.modDisplayContainer.addChild(descriptionOverlay); equipButton.down = function () { return _this4.equipMod(modAssetId); }; tween(enlargedMod, { scaleX: 4, scaleY: 4 }, { duration: 300, easing: tween.backOut }); tween(descriptionOverlay, { alpha: 1 }, { duration: 200, delay: 150 }); bgBlocker.down = function () { return _this4.hideModDisplay(); }; }, hideModDisplay: function hideModDisplay() { var _this5 = this; if (!this.modDisplayContainer) return; tween(this.modDisplayContainer, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (_this5.modDisplayContainer && _this5.modDisplayContainer.parent) { _this5.modDisplayContainer.parent.removeChild(_this5.modDisplayContainer); } _this5.modDisplayContainer = null; _this5.currentlyDisplayedMod = null; } }); }, getEquippedModAsset: function getEquippedModAsset(suit) { return this.equippedMods[suit] || null; } }; /**** * Wave System ****/ var WaveSystem = { playerSpawnTimer: 0, aiSpawnTimer: 0, waveNumber: 1, waveTimer: 0, waveDuration: 1800, spawnInterval: 45, bossSpawned: false, playerBossDefeated: false, aiBossDefeated: false, getChipValue: function getChipValue(waveNumber) { var waveConfigs = [{ maxWave: 1, values: [{ chance: 1, value: 1 }] }, { maxWave: 3, values: [{ chance: 0.2, value: 5 }, { chance: 1, value: 1 }] }, { maxWave: 6, values: [{ chance: 0.35, value: 5 }, { chance: 1, value: 1 }] }, { maxWave: 9, values: [{ chance: 0.65, value: 5 }, { chance: 1, value: 1 }] }, { maxWave: 15, values: [{ chance: 0.15, value: 10 }, { chance: 0.7, value: 5 }, { chance: 1, value: 1 }] }, { maxWave: 19, values: [{ chance: 0.3, value: 10 }, { chance: 0.8, value: 5 }, { chance: 1, value: 1 }] }, { maxWave: Infinity, values: [{ chance: 0.08, value: 25 }, { chance: 0.4, value: 10 }, { chance: 0.85, value: 5 }, { chance: 1, value: 1 }] }]; var config = waveConfigs.find(function (c) { return waveNumber <= c.maxWave; }); var rand = Math.random(); var _iterator = _createForOfIteratorHelper(config.values), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _step$value = _step.value, chance = _step$value.chance, value = _step$value.value; if (rand < chance) return value; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return 1; }, getBossChipValue: function getBossChipValue(waveNumber) { var bossLevel = Math.floor(waveNumber / 10); var baseBossValue = 210; return baseBossValue * Math.pow(2, bossLevel - 1); }, isBossWave: function isBossWave(waveNumber) { return waveNumber % 10 === 0; }, spawnChip: function spawnChip(isPlayerSide) { if (this.isBossWave(this.waveNumber)) { if (!this.bossSpawned) { var bossValue = this.getBossChipValue(this.waveNumber); ChipSpawner.spawnChip(bossValue, isPlayerSide); this.bossSpawned = true; } return; } var chipValue = this.getChipValue(this.waveNumber); ChipSpawner.spawnChip(chipValue, isPlayerSide); }, update: function update() { this.waveTimer++; if (this.waveTimer >= this.waveDuration) { if (this.isBossWave(this.waveNumber) && (!this.playerBossDefeated || !this.aiBossDefeated)) { // Wait for boss defeat } else { if (this.isBossWave(this.waveNumber) && this.playerBossDefeated && this.aiBossDefeated) { createFloatingText('ROUND COMPLETE!', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0x00ff00); this.playerBossDefeated = false; this.aiBossDefeated = false; } this.waveTimer = 0; this.waveNumber++; this.playerSpawnTimer = 0; this.aiSpawnTimer = 0; this.bossSpawned = false; return; } } if (this.isBossWave(this.waveNumber)) { if (!this.bossSpawned && this.waveTimer === 1) { var bossValue = this.getBossChipValue(this.waveNumber); ChipSpawner.spawnChip(bossValue, true); ChipSpawner.spawnChip(bossValue, false); this.bossSpawned = true; } if (this.bossSpawned && this.waveTimer > 120) { if (!this.playerBossDefeated && activePlayerChips.length === 0) { this.playerBossDefeated = true; createFloatingText('BOSS DEFEATED!', SCREEN_WIDTH / 2, PLAYER_AREA_Y + SLOT_HEIGHT, 0xffd700); } if (!this.aiBossDefeated && activeAIChips.length === 0) { this.aiBossDefeated = true; createFloatingText('AI BOSS DEFEATED!', SCREEN_WIDTH / 2, AI_AREA_Y + SLOT_HEIGHT, 0xffd700); } if (this.playerBossDefeated && this.aiBossDefeated) { this.waveTimer = this.waveDuration - 1; } } return; } // Normal wave spawning var currentSpawnInterval = this.waveNumber === 1 ? 90 : Math.max(45, this.spawnInterval - Math.floor((this.waveNumber - 2) * 2)); this.playerSpawnTimer++; if (this.playerSpawnTimer >= currentSpawnInterval) { this.playerSpawnTimer = 0; this.spawnChip(true); } this.aiSpawnTimer++; if (this.aiSpawnTimer >= currentSpawnInterval) { this.aiSpawnTimer = 0; this.spawnChip(false); } } }; /**** * Game State ****/ var gameState = { playerChips: 200, aiChips: 200, playerLives: 3, aiLives: 3, isPlayerTurn: true, dealCost: 25, dealCount: 0, playerDeck: [], playerHand: [], playerPlayArea: [], aiDeck: [], aiPlayArea: [] }; /**** * Game Variables ****/ var currentGameState = 'start'; var startScreenElements = []; var gameElements = []; var activePlayerChips = []; var activeAIChips = []; var activeBullets = []; var playerHandNameTexts = []; var backgroundSuits = []; var selectedCard = null; var isDragging = false; var originalCardPosition = null; var activePlayerChipsContainer = new Container(); var activeAIChipsContainer = new Container(); var playerLifeHearts = []; var aiLifeHearts = []; var opponentNameText = null; var playerNameText = null; var lastPlayerLives = 0; var lastAiLives = 0; /**** * Initialize Layers ****/ var gameLayer = new Container(); var floorBackground = LK.getAsset('floorbackround', { anchorX: 0.5, anchorY: 0.5 }); floorBackground.x = SCREEN_WIDTH / 2; floorBackground.y = SCREEN_HEIGHT / 2; gameLayer.addChild(floorBackground); var uiLayer = new Container(); game.addChild(gameLayer); game.addChild(uiLayer); /**** * UI Elements ****/ var playerChipsText = new Text2('Chips: 200', { size: 50, fill: 0xffd700, weight: 800, stroke: 0x000000, strokeThickness: 0 }); playerChipsText.x = 50; playerChipsText.y = SCREEN_HEIGHT - 120; playerChipsText.visible = false; uiLayer.addChild(playerChipsText); gameElements.push(playerChipsText); var waveText = new Text2('Wave: 1', { size: 40, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 0 }); waveText.x = SCREEN_WIDTH - 200; waveText.y = 50; waveText.visible = false; uiLayer.addChild(waveText); gameElements.push(waveText); // Deal/Discard button var discardAreaContainer = new Container(); var discardAreaGraphic = discardAreaContainer.attachAsset('dealButton', { anchorX: 0.5, anchorY: 0.5 }); var discardText = new Text2('-25', { size: 50, fill: 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 0 }); discardText.anchor.set(0.5, 0.5); discardText.y = 110; discardAreaContainer.addChild(discardText); var handStartXForDiscard = (SCREEN_WIDTH - HAND_WIDTH) / 2; var discardX = Math.min(handStartXForDiscard + HAND_WIDTH + 30 + DEAL_SLOT_WIDTH / 2 + 20, SCREEN_WIDTH - DEAL_SLOT_WIDTH / 2 - 20); discardAreaContainer.x = discardX; discardAreaContainer.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2; discardAreaContainer.visible = false; uiLayer.addChild(discardAreaContainer); gameElements.push(discardAreaContainer); discardAreaContainer.down = function () { if (!isDragging && gameState.playerChips >= gameState.dealCost) { dealNewHand(); } }; /**** * Game Functions ****/ function calculateGreedBonus(isPlayerSide, baseChipsEarned) { if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') return 0; var playArea = isPlayerSide ? gameState.playerPlayArea : gameState.aiPlayArea; var diamondCount = 0; for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = playArea[row][col]; if (card && card.cardData.suit === 'diamonds') diamondCount++; } } var bonusPercentage = diamondCount * 0.05; return Math.ceil(baseChipsEarned * bonusPercentage); } function createStartScreen() { ModSystem.init(); startScreenElements.forEach(function (element) { if (element.parent) element.parent.removeChild(element); }); startScreenElements = []; // Background animation var startScreenAnimContainer = new Container(); uiLayer.addChild(startScreenAnimContainer); startScreenElements.push(startScreenAnimContainer); var suitAssets = ['heartSuit', 'diamondSuit', 'clubSuit', 'spadeSuit']; var spacing = 400; var numCols = Math.ceil(SCREEN_WIDTH / spacing) + 3; var numRows = Math.ceil(SCREEN_HEIGHT / spacing) + 3; backgroundSuits = []; for (var row = 0; row < numRows; row++) { for (var col = 0; col < numCols; col++) { var suitIndex = (row + col) % suitAssets.length; var suit = LK.getAsset(suitAssets[suitIndex], { anchorX: 0.5, anchorY: 0.5 }); suit.x = col * spacing - spacing; suit.y = row * spacing - spacing; suit.alpha = 0.8; suit.scale.set(1.5); suit.baseX = col * spacing; suit.baseY = row * spacing; startScreenAnimContainer.addChild(suit); backgroundSuits.push(suit); } } // Logo var gameLogo = LK.getAsset('titleLogo', { anchorX: 0.5, anchorY: 0.5 }); gameLogo.x = SCREEN_WIDTH / 2; gameLogo.y = SCREEN_HEIGHT / 2 - 200; uiLayer.addChild(gameLogo); startScreenElements.push(gameLogo); // Bottom bar var startBottomBar = LK.getAsset('bottomBar', { anchorX: 0.5, anchorY: 1 }); startBottomBar.x = SCREEN_WIDTH / 2; startBottomBar.y = SCREEN_HEIGHT; uiLayer.addChild(startBottomBar); startScreenElements.push(startBottomBar); // Selection highlight var titleScreenSelection = LK.getAsset('titleScreenSelection', { anchorX: 0.5, anchorY: 1 }); titleScreenSelection.alpha = 0.3; uiLayer.addChild(titleScreenSelection); startScreenElements.push(titleScreenSelection); var currentTitleScreenSelection = "battle"; // Play button and icon var playButton = LK.getAsset('playButton', { anchorX: 0.5, anchorY: 0.5, interactive: true }); playButton.x = SCREEN_WIDTH / 2; playButton.y = SCREEN_HEIGHT - 100; var battleIcon = LK.getAsset('battleIcon', { anchorX: 0.5, anchorY: 1, interactive: true }); battleIcon.x = playButton.x; battleIcon.y = playButton.y - playButton.height / 2 - 10; uiLayer.addChild(battleIcon); uiLayer.addChild(playButton); startScreenElements.push(battleIcon); startScreenElements.push(playButton); // Mods button and icon var suitModButton = LK.getAsset('suitModButton', { anchorX: 0.5, anchorY: 0.5, interactive: true }); suitModButton.x = playButton.x + playButton.width / 2 + 50 + suitModButton.width / 2; suitModButton.y = playButton.y; var modsIcon = LK.getAsset('modsIcon', { anchorX: 0.5, anchorY: 1, interactive: true }); modsIcon.x = suitModButton.x; modsIcon.y = suitModButton.y - suitModButton.height / 2 - 10; uiLayer.addChild(modsIcon); uiLayer.addChild(suitModButton); startScreenElements.push(modsIcon); startScreenElements.push(suitModButton); // Selection highlight functions function updateTitleScreenSelectionHighlight() { var targetX, targetY, targetW, targetH; if (currentTitleScreenSelection === "battle") { targetX = playButton.x; targetY = playButton.y + playButton.height / 2; targetW = playButton.width * 1.1; targetH = playButton.height + battleIcon.height + 20; } else { targetX = suitModButton.x; targetY = suitModButton.y + suitModButton.height / 2; targetW = suitModButton.width * 1.1; targetH = suitModButton.height + modsIcon.height + 20; } tween.stop(titleScreenSelection); tween(titleScreenSelection, { x: targetX, y: targetY, width: targetW, height: targetH }, { duration: 180, easing: tween.cubicOut }); } // Set initial highlight titleScreenSelection.width = playButton.width * 1.2; titleScreenSelection.height = playButton.height + battleIcon.height + 20; titleScreenSelection.x = playButton.x; titleScreenSelection.y = playButton.y + playButton.height / 2; // Button handlers playButton.down = battleIcon.down = function () { if (currentTitleScreenSelection !== "battle") { currentTitleScreenSelection = "battle"; updateTitleScreenSelectionHighlight(); } startGame(); }; suitModButton.down = modsIcon.down = function () { if (currentTitleScreenSelection !== "mods") { currentTitleScreenSelection = "mods"; updateTitleScreenSelectionHighlight(); } modsContainer.visible = !modsContainer.visible; gameLogo.visible = !modsContainer.visible; if (modsContainer.visible) ModSystem.updateTopSuitDisplay(); }; // Mods container var modsContainer = new Container(); modsContainer.visible = false; uiLayer.addChild(modsContainer); startScreenElements.push(modsContainer); var totalBarWidth = SCREEN_WIDTH - 200; var suitSpacing = totalBarWidth / suitAssets.length; var startX = SCREEN_WIDTH / 2 - totalBarWidth / 2 + suitSpacing / 2; var yOffset = SCREEN_HEIGHT * 0.1; var circleY = 300 + yOffset; ModSystem.topSuitGraphics = []; for (var i = 0; i < suitAssets.length; i++) { var suitContainer = new Container(); var circleBg = LK.getAsset('card', { anchorX: 0.5, anchorY: 0.5 }); circleBg.scale.set(1.5); suitContainer.addChild(circleBg); suitContainer.x = startX + i * suitSpacing; suitContainer.y = circleY - SCREEN_HEIGHT * 0.05; modsContainer.addChild(suitContainer); ModSystem.topSuitGraphics.push(suitContainer); } var lineBreak = LK.getAsset('lineBreak', { anchorX: 0.5, anchorY: 0.5 }); lineBreak.x = SCREEN_WIDTH / 2; lineBreak.y = circleY + 360 - SCREEN_HEIGHT * 0.05; modsContainer.addChild(lineBreak); // Mod grid var gridContainer = new Container(); gridContainer.x = SCREEN_WIDTH / 2; gridContainer.y = circleY + 450 - SCREEN_HEIGHT * 0.05; modsContainer.addChild(gridContainer); var cardForSizing = LK.getAsset('card', {}); var cellWidth = cardForSizing.width; var cellHeight = cardForSizing.height; var colSpacing = 160; var rowSpacing = 140; var gridTotalWidth = 4 * cellWidth + 3 * colSpacing; var gridStartX = -gridTotalWidth / 2; var gridStartY = 50; var suitModAssets = ['burnHeartMod', 'chipsDiamondMod', 'spreadClubMod', 'freezeSpadeMod']; for (var r = 0; r < 3; r++) { for (var c = 0; c < 4; c++) { var cell = LK.getAsset('card', { anchorX: 0, anchorY: 0 }); cell.scale.set(1.3); cell.x = gridStartX + c * (cellWidth + colSpacing); cell.y = gridStartY + r * (cellHeight + rowSpacing); gridContainer.addChild(cell); if (r === 0 && suitModAssets[c]) { var modAsset = LK.getAsset(suitModAssets[c], { anchorX: 0.5, anchorY: 0.5 }); modAsset.x = cell.x + cell.width * cell.scale.x / 2; modAsset.y = cell.y + cell.height * cell.scale.y / 2; gridContainer.addChild(modAsset); cell.interactive = true; cell.down = function (assetId, modRef) { return function () { return ModSystem.showModDisplay(assetId, modRef.x, modRef.y); }; }(suitModAssets[c], modAsset); } } } currentGameState = 'start'; } function startGame() { startScreenElements.forEach(function (element) { if (element.parent) element.parent.removeChild(element); }); startScreenElements = []; backgroundSuits = []; gameElements.forEach(function (element) { return element.visible = true; }); currentGameState = 'playing'; initializeGame(); } function initializeGame() { ModSystem.init(); PoolManager.init(); PathSystem.init(); // Initialize play areas gameState.playerPlayArea = Array(PLAY_AREA_ROWS).fill().map(function () { return Array(PLAY_AREA_COLS).fill(null); }); gameState.aiPlayArea = Array(PLAY_AREA_ROWS).fill().map(function () { return Array(PLAY_AREA_COLS).fill(null); }); playerHandNameTexts = [null, null]; // Create decks gameState.playerDeck = CardSystem.createDeck(); gameState.aiDeck = CardSystem.createDeck(); // Setup UI drawPlayAreas(); createLifeDisplays(); lastPlayerLives = gameState.playerLives; lastAiLives = gameState.aiLives; // Initialize player hand gameState.playerHand = Array(5).fill(null); // Start spawning LK.setTimeout(function () { ChipSpawner.spawnChip(1, true); ChipSpawner.spawnChip(1, false); }, 1000); } function createLifeDisplays() { // Clean up existing [opponentNameText, playerNameText].concat(_toConsumableArray(playerLifeHearts), _toConsumableArray(aiLifeHearts)).forEach(function (element) { if (element && element.parent) element.parent.removeChild(element); }); playerLifeHearts = []; aiLifeHearts = []; var heartSpacing = 110; var heartScale = 0.5; var startX_AI = 130; var startX_Player = SCREEN_WIDTH - 130; var yPos = SCREEN_HEIGHT / 2 - 127; var labelYPos = yPos - 60; // Create name labels var totalAIHeartsWidth = (gameState.aiLives - 1) * heartSpacing; opponentNameText = new Text2('Opponent', { size: 40, fill: 0xffffff, weight: 800 }); opponentNameText.anchor.set(0.5, 1); opponentNameText.x = startX_AI + totalAIHeartsWidth / 2; opponentNameText.y = labelYPos; uiLayer.addChild(opponentNameText); var totalPlayerHeartsWidth = (gameState.playerLives - 1) * heartSpacing; playerNameText = new Text2('Player', { size: 40, fill: 0xffffff, weight: 800 }); playerNameText.anchor.set(0.5, 1); playerNameText.x = startX_Player - totalPlayerHeartsWidth / 2; playerNameText.y = labelYPos; uiLayer.addChild(playerNameText); // Create hearts for (var i = 0; i < gameState.aiLives; i++) { var heart = LK.getAsset('heartSuit', { anchorX: 0.5, anchorY: 0.5, scaleX: heartScale, scaleY: heartScale }); heart.x = startX_AI + i * heartSpacing; heart.y = yPos; uiLayer.addChild(heart); aiLifeHearts.push(heart); } for (var i = 0; i < gameState.playerLives; i++) { var heart = LK.getAsset('heartSuit', { anchorX: 0.5, anchorY: 0.5, scaleX: heartScale, scaleY: heartScale }); heart.x = startX_Player - i * heartSpacing; heart.y = yPos; uiLayer.addChild(heart); playerLifeHearts.push(heart); } } function dealNewHand() { if (gameState.playerChips < gameState.dealCost) return; var emptySlotIndex = gameState.playerHand.findIndex(function (card) { return !card; }); if (emptySlotIndex === -1) return; gameState.playerChips -= gameState.dealCost; gameState.dealCount++; gameState.dealCost = Math.floor(25 * Math.pow(1.05, gameState.dealCount)); if (gameState.playerDeck.length === 0) { gameState.playerDeck = CardSystem.createDeck(); } var cardData = gameState.playerDeck.pop(); var card = new Card(cardData); var startLevel = Math.floor((WaveSystem.waveNumber - 1) / 10) + 1; card.setLevel(startLevel); var slotPos = getHandSlotPosition(emptySlotIndex); var startX = slotPos.x + (Math.random() - 0.5) * 50; var startY = SCREEN_HEIGHT + DEAL_SLOT_HEIGHT; card.activate(startX, startY, false, true); card.rotation = 0; card.scaleX = 1; card.scaleY = 0.01; uiLayer.addChild(card); gameState.playerHand[emptySlotIndex] = card; tween(card, { x: slotPos.x, y: slotPos.y, scaleY: 1.2 }, Object.assign({}, ANIMATION_CONFIGS.cardDeal, { delay: emptySlotIndex * 80, onFinish: function onFinish() { return tween(card, { scaleY: 1 }, ANIMATION_CONFIGS.cardSettle); } })); updateUI(); } function updateUI() { playerChipsText.setText('Chips: ' + formatNumberWithSuffix(gameState.playerChips)); if (!isDragging) { discardText.setText('-' + formatNumberWithSuffix(gameState.dealCost)); discardText.fill = 0xffffff; if (gameState.playerChips >= gameState.dealCost) { discardAreaGraphic.tint = 0xffffff; discardAreaGraphic.alpha = 1.0; } else { discardAreaGraphic.tint = 0x666666; discardAreaGraphic.alpha = 1.0; } } waveText.setText('Wave: ' + WaveSystem.waveNumber); } function drawPlayAreas() { // Player play area for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var slot = new Container(); var slotGraphics = slot.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); slotGraphics.alpha = 0.5; slot.x = PLAYER_AREA_X + col * SLOT_WIDTH + SLOT_WIDTH / 2; slot.y = PLAYER_AREA_Y + row * SLOT_HEIGHT + SLOT_HEIGHT / 2; gameLayer.addChild(slot); } } // AI play area for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var slot = new Container(); var slotGraphics = slot.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); slotGraphics.alpha = 0.5; slotGraphics.tint = 0xff8888; slot.x = AI_AREA_X + col * SLOT_WIDTH + SLOT_WIDTH / 2; slot.y = AI_AREA_Y + row * SLOT_HEIGHT + SLOT_HEIGHT / 2; gameLayer.addChild(slot); } } // Player hand slots for (var i = 0; i < 5; i++) { var dealSlot = new Container(); var dealSlotGraphics = dealSlot.attachAsset('dealSlot', { anchorX: 0.5, anchorY: 0.5 }); dealSlotGraphics.alpha = 0.5; var slotPos = getHandSlotPosition(i); dealSlot.x = slotPos.x; dealSlot.y = slotPos.y; gameLayer.addChild(dealSlot); } } function getSlotPosition(row, col, isPlayerArea) { var baseX = isPlayerArea ? PLAYER_AREA_X : AI_AREA_X; var baseY = isPlayerArea ? PLAYER_AREA_Y : AI_AREA_Y; return { x: baseX + col * SLOT_WIDTH + SLOT_WIDTH / 2, y: baseY + row * SLOT_HEIGHT + SLOT_HEIGHT / 2 }; } function getSlotFromPosition(x, y) { // Check player play area if (x >= PLAYER_AREA_X && x <= PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH && y >= PLAYER_AREA_Y && y <= PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT) { var col = Math.floor((x - PLAYER_AREA_X) / SLOT_WIDTH); var row = Math.floor((y - PLAYER_AREA_Y) / SLOT_HEIGHT); if (col >= 0 && col < PLAY_AREA_COLS && row >= 0 && row < PLAY_AREA_ROWS) { return { area: 'player', row: row, col: col }; } } // Check player hand area if (y >= PLAYER_DEAL_AREA_Y && y <= PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT) { for (var i = 0; i < 5; i++) { var slotPos = getHandSlotPosition(i); var slotXStart = slotPos.x - DEAL_SLOT_WIDTH / 2; var slotXEnd = slotPos.x + DEAL_SLOT_WIDTH / 2; if (x >= slotXStart && x <= slotXEnd) { return { area: 'hand', index: i }; } } } // Check discard area if (discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2) { return { area: 'discard' }; } return null; } function evaluateRowHand(row, isPlayerArea) { var playArea = isPlayerArea ? gameState.playerPlayArea : gameState.aiPlayArea; var cards = []; for (var col = 0; col < PLAY_AREA_COLS; col++) { if (playArea[row][col]) cards.push(playArea[row][col]); } return CardSystem.evaluatePokerHand(cards); } function updateHandNameDisplay(row, handEval) { var existingText = playerHandNameTexts[row]; var shouldShowText = handEval.strength > 1; if (shouldShowText) { var handName = handEval.type.replace(/_/g, ' ').toUpperCase(); if (existingText) { if (existingText.text !== handName) { existingText.setText(handName); } existingText.visible = true; } else { var newText = new Text2(handName, { size: 50, fill: 0xffffff, weight: '800', stroke: 0x000000, strokeThickness: 0 }); newText.anchor.set(0.5, 0); newText.alpha = 0.8; newText.x = PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH / 2; newText.y = PLAYER_AREA_Y + (row + 1) * SLOT_HEIGHT - 20; uiLayer.addChild(newText); playerHandNameTexts[row] = newText; } } else if (existingText) { existingText.visible = false; } } function applyHandBonuses() { // Player cards for (var row = 0; row < PLAY_AREA_ROWS; row++) { var handEval = evaluateRowHand(row, true); updateHandNameDisplay(row, handEval); var contributingCards = handEval.contributingCards || []; for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = gameState.playerPlayArea[row][col]; if (card) { card.handBonus = handEval.multiplier; card.calculateStats(); card.redOutline.visible = handEval.strength > 1 && contributingCards.includes(card); } } } // AI cards AISystem.applyAIHandBonuses(); } /**** * AI System ****/ var AISystem = { thinkTimer: 0, thinkDelay: 60, update: function update() { this.thinkTimer++; if (this.thinkTimer >= this.thinkDelay) { this.thinkTimer = 0; this.makeMove(); } }, shouldDeal: function shouldDeal() { if (gameState.aiChips < gameState.dealCost) return false; return this.countEmptySlots() > 0; }, countEmptySlots: function countEmptySlots() { var count = 0; for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { if (!gameState.aiPlayArea[row][col]) count++; } } return count; }, makeMove: function makeMove() { if (this.tryMergeCards()) return; if (this.tryPlaceCards()) return; if (this.optimizeCardPositions()) return; if (this.shouldDeal()) this.dealAIHand(); }, tryMergeCards: function tryMergeCards() { for (var row1 = 0; row1 < PLAY_AREA_ROWS; row1++) { for (var col1 = 0; col1 < PLAY_AREA_COLS; col1++) { var card1 = gameState.aiPlayArea[row1][col1]; if (!card1) continue; for (var row2 = 0; row2 < PLAY_AREA_ROWS; row2++) { for (var col2 = 0; col2 < PLAY_AREA_COLS; col2++) { if (row1 === row2 && col1 === col2) continue; var card2 = gameState.aiPlayArea[row2][col2]; if (!card2) continue; if (card1.canMergeWith(card2)) { this.mergeCards(card1, card2, row1, col1, row2, col2); return true; } } } } } return false; }, mergeCards: function mergeCards(card1, card2, row1, col1, row2, col2) { var mergedCard = card1.mergeWith(card2); if (!mergedCard) return; gameLayer.removeChild(card1); gameLayer.removeChild(card2); gameState.aiPlayArea[row1][col1] = null; gameState.aiPlayArea[row2][col2] = null; var pos = getSlotPosition(row1, col1, false); mergedCard.activate(pos.x, pos.y, true, false); gameLayer.addChild(mergedCard); gameState.aiPlayArea[row1][col1] = mergedCard; mergedCard.alpha = 0; mergedCard.scale.set(1.5); tween(mergedCard, { alpha: 1, scaleX: 1, scaleY: 1 }, ANIMATION_CONFIGS.cardMerge); createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00); this.applyAIHandBonuses(); }, tryPlaceCards: function tryPlaceCards() { return false; // AI deals directly to board }, optimizeCardPositions: function optimizeCardPositions() { return this.tryCompletePokerHands(); }, tryCompletePokerHands: function tryCompletePokerHands() { for (var row = 0; row < PLAY_AREA_ROWS; row++) { var rowCards = []; var emptyPositions = []; for (var col = 0; col < PLAY_AREA_COLS; col++) { if (gameState.aiPlayArea[row][col]) { rowCards.push({ card: gameState.aiPlayArea[row][col], col: col }); } else { emptyPositions.push(col); } } if (rowCards.length >= 3 && emptyPositions.length > 0) { if (this.tryMoveCardToCompleteHand(row, rowCards, emptyPositions[0])) { return true; } } } return false; }, tryMoveCardToCompleteHand: function tryMoveCardToCompleteHand(targetRow, existingCards, targetCol) { for (var row = 0; row < PLAY_AREA_ROWS; row++) { if (row === targetRow) continue; for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = gameState.aiPlayArea[row][col]; if (!card) continue; if (this.cardHelpsHand(card, existingCards) && Math.random() < 0.3) { gameState.aiPlayArea[row][col] = null; gameState.aiPlayArea[targetRow][targetCol] = card; var newPos = getSlotPosition(targetRow, targetCol, false); tween(card, { x: newPos.x, y: newPos.y }, { duration: 300, easing: tween.quadOut }); this.applyAIHandBonuses(); return true; } } } return false; }, cardHelpsHand: function cardHelpsHand(card, existingCards) { var suits = {}; var values = {}; existingCards.forEach(function (_ref3) { var c = _ref3.card; suits[c.cardData.suit] = (suits[c.cardData.suit] || 0) + 1; values[c.cardData.value] = (values[c.cardData.value] || 0) + 1; }); return suits[card.cardData.suit] >= 2 || values[card.cardData.value] >= 1; }, dealAIHand: function dealAIHand() { if (gameState.aiChips < gameState.dealCost) return; gameState.aiChips -= gameState.dealCost; var cardData; do { if (gameState.aiDeck.length === 0) { gameState.aiDeck = CardSystem.createDeck(); } cardData = gameState.aiDeck.pop(); } while (cardData.suit === 'joker'); var card = new Card(cardData); var startLevel = Math.floor((WaveSystem.waveNumber - 1) / 10) + 1; card.setLevel(startLevel); var bestSlot = this.findBestEmptySlot(); if (!bestSlot) return; var pos = getSlotPosition(bestSlot.row, bestSlot.col, false); var startY = -SLOT_HEIGHT; card.activate(pos.x, startY, true, false); card.rotation = Math.PI * 4; card.scale.set(0.1); gameLayer.addChild(card); gameState.aiPlayArea[bestSlot.row][bestSlot.col] = card; tween(card, { y: pos.y, rotation: 0, scaleX: 1, scaleY: 1 }, { duration: 600, easing: tween.quadOut }); this.applyAIHandBonuses(); }, findBestEmptySlot: function findBestEmptySlot() { var emptySlots = []; for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { if (!gameState.aiPlayArea[row][col]) { var score = this.evaluateSlotScore(row, col); emptySlots.push({ row: row, col: col, score: score }); } } } if (emptySlots.length === 0) return null; emptySlots.sort(function (a, b) { return b.score - a.score; }); return emptySlots[0]; }, evaluateSlotScore: function evaluateSlotScore(row, col) { var score = 0; var cardsInRow = 0; for (var c = 0; c < PLAY_AREA_COLS; c++) { if (gameState.aiPlayArea[row][c]) cardsInRow++; } score += cardsInRow * 10; score += (2 - Math.abs(col - 2)) * 2; return score; }, applyAIHandBonuses: function applyAIHandBonuses() { for (var row = 0; row < PLAY_AREA_ROWS; row++) { var handEval = evaluateRowHand(row, false); var contributingCards = handEval.contributingCards || []; for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = gameState.aiPlayArea[row][col]; if (card) { card.handBonus = handEval.multiplier; card.calculateStats(); card.redOutline.visible = handEval.strength > 1 && contributingCards.includes(card); } } } } }; /**** * Input Handling ****/ game.down = function (x, y, obj) { if (currentGameState !== 'playing') return; // Check hand cards for (var i = 0; i < gameState.playerHand.length; i++) { var card = gameState.playerHand[i]; if (!card) continue; var slotPos = getHandSlotPosition(i); if (Math.abs(x - slotPos.x) < DEAL_SLOT_WIDTH / 2 && Math.abs(y - slotPos.y) < DEAL_SLOT_HEIGHT / 2) { selectedCard = card; isDragging = true; originalCardPosition = { area: 'hand', index: i }; uiLayer.addChild(selectedCard); return; } } // Check play area cards for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = gameState.playerPlayArea[row][col]; if (!card) continue; var pos = getSlotPosition(row, col, true); if (Math.abs(x - pos.x) < SLOT_WIDTH / 2 && Math.abs(y - pos.y) < SLOT_HEIGHT / 2) { selectedCard = card; isDragging = true; originalCardPosition = { area: 'player', row: row, col: col }; gameState.playerPlayArea[row][col] = null; gameLayer.addChild(selectedCard); return; } } } }; game.move = function (x, y, obj) { if (currentGameState !== 'playing' || !isDragging || !selectedCard) return; selectedCard.x = x; selectedCard.y = y; // Highlight mergeable cards for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var card = gameState.playerPlayArea[row][col]; if (card) { card.greenOutline.visible = selectedCard.canMergeWith(card); } } } gameState.playerHand.forEach(function (card) { if (card && card !== selectedCard) { card.greenOutline.visible = selectedCard.canMergeWith(card); } }); // Update discard area discardAreaGraphic.tint = 0x440000; var isOverDiscard = discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2; if (isOverDiscard) { discardAreaGraphic.alpha = 1.0; discardText.setText('Sell Card'); discardText.fill = 0xffd700; } else { discardAreaGraphic.alpha = 0.7; discardText.setText('Discard'); discardText.fill = 0x999999; } }; game.up = function (x, y, obj) { if (currentGameState !== 'playing' || !isDragging || !selectedCard) return; isDragging = false; // Clear outlines [].concat(_toConsumableArray(gameState.playerPlayArea.flat()), _toConsumableArray(gameState.playerHand)).filter(function (card) { return card; }).forEach(function (card) { return card.greenOutline.visible = false; }); updateUI(); var targetSlot = getSlotFromPosition(x, y); if (!targetSlot) { returnCardToOriginalPosition(); selectedCard = null; originalCardPosition = null; applyHandBonuses(); return; } // Handle discard if (targetSlot.area === 'discard') { var chipRefund = 5 + selectedCard.level * 2; gameState.playerChips += chipRefund; if (originalCardPosition.area === 'hand') { gameState.playerHand[originalCardPosition.index] = null; } if (selectedCard.parent) selectedCard.parent.removeChild(selectedCard); createFloatingText('+' + formatNumberWithSuffix(chipRefund) + ' Chips', selectedCard.x, selectedCard.y, 0xffd700); selectedCard = null; originalCardPosition = null; updateUI(); applyHandBonuses(); return; } // Handle placement if (targetSlot.area === 'player') { handlePlayAreaPlacement(targetSlot); } else if (targetSlot.area === 'hand') { handleHandPlacement(targetSlot); } selectedCard = null; originalCardPosition = null; applyHandBonuses(); }; function handlePlayAreaPlacement(targetSlot) { var existingCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col]; if (existingCard && selectedCard.canMergeWith(existingCard)) { // Merge cards var mergedCard = selectedCard.mergeWith(existingCard); if (!mergedCard) return; gameLayer.removeChild(existingCard); var handIndex = gameState.playerHand.indexOf(selectedCard); if (handIndex !== -1) { uiLayer.removeChild(selectedCard); gameState.playerHand[handIndex] = null; } else { gameLayer.removeChild(selectedCard); } var pos = getSlotPosition(targetSlot.row, targetSlot.col, true); mergedCard.activate(pos.x, pos.y, true); gameLayer.addChild(mergedCard); gameState.playerPlayArea[targetSlot.row][targetSlot.col] = mergedCard; mergedCard.alpha = 0; mergedCard.scale.set(2); tween(mergedCard, { alpha: 1, scaleX: 1, scaleY: 1 }, ANIMATION_CONFIGS.cardMerge); createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00); } else if (!existingCard) { // Place in empty slot if (selectedCard.cardData.suit === 'joker') { createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00); returnCardToOriginalPosition(); return; } var pos = getSlotPosition(targetSlot.row, targetSlot.col, true); selectedCard.activate(pos.x, pos.y, true); var handIndex = gameState.playerHand.indexOf(selectedCard); if (handIndex !== -1) { uiLayer.removeChild(selectedCard); gameLayer.addChild(selectedCard); gameState.playerHand[handIndex] = null; } gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard; } else { // Swap cards if (selectedCard.cardData.suit === 'joker') { createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00); returnCardToOriginalPosition(); return; } var swappedCard = existingCard; // Place selected card var pos1 = getSlotPosition(targetSlot.row, targetSlot.col, true); selectedCard.activate(pos1.x, pos1.y, true); var handIndex = gameState.playerHand.indexOf(selectedCard); if (handIndex !== -1) { uiLayer.removeChild(selectedCard); gameLayer.addChild(selectedCard); gameState.playerHand[handIndex] = null; } gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard; // Place swapped card back if (originalCardPosition.area === 'player') { var pos2 = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true); swappedCard.activate(pos2.x, pos2.y, true); gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard; } else { var slotPos = getHandSlotPosition(originalCardPosition.index); swappedCard.activate(slotPos.x, slotPos.y, false, true); gameLayer.removeChild(swappedCard); uiLayer.addChild(swappedCard); gameState.playerHand[originalCardPosition.index] = swappedCard; } } } function handleHandPlacement(targetSlot) { var existingCard = gameState.playerHand[targetSlot.index]; if (existingCard && existingCard !== selectedCard && selectedCard.canMergeWith(existingCard)) { // Merge in hand var mergedCard = selectedCard.mergeWith(existingCard); if (!mergedCard) { returnCardToOriginalPosition(); return; } // Remove old cards var handIndex1 = gameState.playerHand.indexOf(selectedCard); if (handIndex1 !== -1) { uiLayer.removeChild(selectedCard); gameState.playerHand[handIndex1] = null; } else { gameLayer.removeChild(selectedCard); } var handIndex2 = gameState.playerHand.indexOf(existingCard); if (handIndex2 !== -1) { uiLayer.removeChild(existingCard); gameState.playerHand[handIndex2] = null; } // Place merged card var slotPos = getHandSlotPosition(targetSlot.index); mergedCard.activate(slotPos.x, slotPos.y, false, true); uiLayer.addChild(mergedCard); gameState.playerHand[targetSlot.index] = mergedCard; mergedCard.alpha = 0; mergedCard.scale.set(2); tween(mergedCard, { alpha: 1, scaleX: 1, scaleY: 1 }, ANIMATION_CONFIGS.cardMerge); createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00); } else if (existingCard && existingCard !== selectedCard) { // Swap cards var swappedCard = existingCard; // Move selected card to target slot var slotPos1 = getHandSlotPosition(targetSlot.index); selectedCard.activate(slotPos1.x, slotPos1.y, false, true); if (originalCardPosition.area === 'player') { gameLayer.removeChild(selectedCard); uiLayer.addChild(selectedCard); } else { gameState.playerHand[originalCardPosition.index] = null; } gameState.playerHand[targetSlot.index] = selectedCard; // Move swapped card to original position if (originalCardPosition.area === 'player') { var origPos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true); swappedCard.activate(origPos.x, origPos.y, true); uiLayer.removeChild(swappedCard); gameLayer.addChild(swappedCard); gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard; } else { var slotPos2 = getHandSlotPosition(originalCardPosition.index); swappedCard.activate(slotPos2.x, slotPos2.y, false, true); gameState.playerHand[originalCardPosition.index] = swappedCard; } } else { returnCardToOriginalPosition(); } } function returnCardToOriginalPosition() { if (!selectedCard || !originalCardPosition) return; if (originalCardPosition.area === 'hand') { var slotPos = getHandSlotPosition(originalCardPosition.index); selectedCard.x = slotPos.x; selectedCard.y = slotPos.y; gameState.playerHand[originalCardPosition.index] = selectedCard; } else if (originalCardPosition.area === 'player') { var pos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true); selectedCard.x = pos.x; selectedCard.y = pos.y; gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = selectedCard; } } function createFloatingText(text, x, y, color, size) { var textOptions = { size: size || 40, fill: color || 0xffffff, weight: 800, stroke: 0x000000, strokeThickness: 0 }; if (color === 0x00ff00) { textOptions.strokeThickness = 6; textOptions.size = (size || 40) * 2; } var floatingText = new Text2(text, textOptions); floatingText.anchor.set(0.5, 0.5); floatingText.x = x; floatingText.y = y; floatingText.alpha = 1; uiLayer.addChild(floatingText); var animationDistance = size && size > 40 ? 120 : 80; var animationDuration = size && size > 40 ? 2000 : 1500; tween(floatingText, { y: y - animationDistance, alpha: 0 }, { duration: animationDuration, easing: tween.quadOut, onFinish: function onFinish() { return uiLayer.removeChild(floatingText); } }); } /**** * Main Game Loop ****/ game.update = function () { if (currentGameState !== 'playing') { if (currentGameState === 'start' && backgroundSuits.length > 0) { var speed = 0.5; var spacing = 400; var numCols = Math.ceil(SCREEN_WIDTH / spacing) + 3; var numRows = Math.ceil(SCREEN_HEIGHT / spacing) + 3; var patternWidth = numCols * spacing; var patternHeight = numRows * spacing; backgroundSuits.forEach(function (suit) { suit.x += speed; suit.y += speed; if (suit.x > SCREEN_WIDTH + spacing * 1.5) { suit.x -= patternWidth; } if (suit.y > SCREEN_HEIGHT + spacing * 1.5) { suit.y -= patternHeight; } }); } return; } // Clean up dead chips activePlayerChips = activePlayerChips.filter(function (chip) { return chip.active && chip.health > 0; }); activeAIChips = activeAIChips.filter(function (chip) { return chip.active && chip.health > 0; }); // Update systems WaveSystem.update(); // Update entities activePlayerChips.forEach(function (chip) { return chip.update(); }); activeAIChips.forEach(function (chip) { return chip.update(); }); activeBullets.forEach(function (bullet) { return bullet.update(); }); // Update cards for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var playerCard = gameState.playerPlayArea[row][col]; var aiCard = gameState.aiPlayArea[row][col]; if (playerCard) playerCard.update(); if (aiCard) aiCard.update(); } } // Update AI AISystem.update(); // Update life displays while (gameState.playerLives < lastPlayerLives) { lastPlayerLives--; var heartToFade = playerLifeHearts[lastPlayerLives]; if (heartToFade) { tween(heartToFade, { alpha: 0.2 }, { duration: 500 }); } } while (gameState.aiLives < lastAiLives) { lastAiLives--; var heartToFade = aiLifeHearts[lastAiLives]; if (heartToFade) { tween(heartToFade, { alpha: 0.2 }, { duration: 500 }); } } // Check win/lose if (gameState.playerLives <= 0) { showGameOver(false); } else if (gameState.aiLives <= 0) { showGameOver(true); } updateUI(); }; /**** * Game Over ****/ function showGameOver(playerWon) { if (playerWon) { LK.showYouWin(); } else { LK.showGameOver(); } } var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0 }); background.x = SCREEN_WIDTH / 2; background.y = 50; background.visible = false; gameLayer.addChild(background); gameElements.push(background); // Chip racks var chipRack1 = LK.getAsset('chipRack', { anchorX: 0.5, anchorY: 0 }); var rackWidth = chipRack1.width; chipRack1.x = SCREEN_WIDTH / 2 - rackWidth / 2; chipRack1.y = 60 - chipRack1.height * 0.75 + 30; chipRack1.visible = false; gameLayer.addChild(chipRack1); gameElements.push(chipRack1); var chipRack2 = LK.getAsset('chipRack', { anchorX: 0.5, anchorY: 0 }); chipRack2.x = SCREEN_WIDTH / 2 + rackWidth / 2; chipRack2.y = 60 - chipRack2.height * 0.75 + 30; chipRack2.visible = false; gameLayer.addChild(chipRack2); gameElements.push(chipRack2); // Border var border = LK.getAsset('border', { anchorX: 0.5, anchorY: 0.5 }); border.x = SCREEN_WIDTH / 2; border.y = SCREEN_HEIGHT / 2; border.visible = false; gameLayer.addChild(border); gameElements.push(border); // Bottom bar var bottomBar = LK.getAsset('bottomBar', { anchorX: 0.5, anchorY: 1 }); bottomBar.x = SCREEN_WIDTH / 2; bottomBar.y = SCREEN_HEIGHT; bottomBar.visible = false; gameLayer.addChild(bottomBar); gameElements.push(bottomBar); // Chip stack var chipStack = LK.getAsset('chipStack', { anchorX: 0.5, anchorY: 1 }); chipStack.x = playerChipsText.x + playerChipsText.width / 2; chipStack.y = playerChipsText.y - 10; chipStack.visible = false; uiLayer.addChild(chipStack); gameElements.push(chipStack); // Initialize containers gameLayer.addChildAt(activePlayerChipsContainer, gameLayer.getChildIndex(bottomBar)); gameLayer.addChildAt(activeAIChipsContainer, gameLayer.getChildIndex(bottomBar)); // Start game createStartScreen();
===================================================================
--- original.js
+++ change.js
@@ -19,33 +19,21 @@
self.isPlayerCard = true;
self.isSeekingLastPosition = false;
self.targetLastX = 0;
self.targetLastY = 0;
+ self.suit = null;
var currentGraphic = null;
- var suitGraphics = {
- 'hearts': self.attachAsset(ModSystem.getEquippedModAsset('hearts') || 'heartSuit', {
+ var suitGraphics = {};
+ // Initialize suit graphics
+ ['hearts', 'diamonds', 'clubs', 'spades'].forEach(function (suit) {
+ var graphic = self.attachAsset(ModSystem.getEquippedModAsset(suit) || suit + 'Suit', {
anchorX: 0.5,
anchorY: 0.5
- }),
- 'diamonds': self.attachAsset(ModSystem.getEquippedModAsset('diamonds') || 'diamondSuit', {
- anchorX: 0.5,
- anchorY: 0.5
- }),
- 'clubs': self.attachAsset(ModSystem.getEquippedModAsset('clubs') || 'clubSuit', {
- anchorX: 0.5,
- anchorY: 0.5
- }),
- 'spades': self.attachAsset(ModSystem.getEquippedModAsset('spades') || 'spadeSuit', {
- anchorX: 0.5,
- anchorY: 0.5
- })
- };
- // Set scale and hide all suit graphics initially.
- for (var suit in suitGraphics) {
- var graphic = suitGraphics[suit];
- graphic.scale.set(0.3); // Small bullets
+ });
+ graphic.scale.set(0.3);
graphic.visible = false;
- }
+ suitGraphics[suit] = graphic;
+ });
self.activate = function (startX, startY, target, damage, suit, isPlayerCard) {
self.active = true;
self.visible = true;
self.x = startX;
@@ -58,190 +46,127 @@
if (self.target) {
self.targetLastX = self.target.x;
self.targetLastY = self.target.y;
}
- if (currentGraphic) {
- currentGraphic.visible = false;
- }
- if (suit && suitGraphics[suit]) {
- currentGraphic = suitGraphics[suit];
- } else {
- // Default to hearts if no suit specified
- currentGraphic = suitGraphics['hearts'];
- }
+ if (currentGraphic) currentGraphic.visible = false;
+ currentGraphic = suitGraphics[self.suit] || suitGraphics['hearts'];
currentGraphic.visible = true;
};
self.findNewTarget = function () {
- // Use the same targeting logic as cards, but from bullet's perspective
var targets = self.isPlayerCard ? activePlayerChips : activeAIChips;
var bestTarget = null;
var shortestDistance = Infinity;
- for (var i = 0; i < targets.length; i++) {
- if (targets[i].active) {
- var dx = targets[i].x - self.x;
- var dy = targets[i].y - self.y;
+ targets.forEach(function (chip) {
+ if (chip.active) {
+ var dx = chip.x - self.x;
+ var dy = chip.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
shortestDistance = distance;
- bestTarget = targets[i];
+ bestTarget = chip;
}
}
- }
+ });
return bestTarget;
};
self.update = function () {
- if (!self.active) {
- return;
- }
- // If target is destroyed, try to find a new target instead of seeking last position
+ if (!self.active) return;
if (self.target && !self.target.active) {
var newTarget = self.findNewTarget();
if (newTarget) {
self.target = newTarget;
self.targetLastX = newTarget.x;
self.targetLastY = newTarget.y;
- // Continue with normal targeting logic
} else {
- // No new target available, seek last position
self.isSeekingLastPosition = true;
self.target = null;
}
}
- // If seeking, move towards the last position
if (self.isSeekingLastPosition) {
var dx = self.targetLastX - self.x;
var dy = self.targetLastY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
- // Arrived at destination, recycle bullet
self.isSeekingLastPosition = false;
- if (currentGraphic) {
- currentGraphic.visible = false;
- }
+ if (currentGraphic) currentGraphic.visible = false;
PoolManager.returnBullet(self);
} else {
- // Move towards destination
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
} else if (self.target) {
- // Store previous position for continuous collision detection
var prevX = self.x;
var prevY = self.y;
- // Update last known position
self.targetLastX = self.target.x;
self.targetLastY = self.target.y;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- // Calculate where bullet will be after this frame
var angle = Math.atan2(dy, dx);
var newX = self.x + Math.cos(angle) * self.speed;
var newY = self.y + Math.sin(angle) * self.speed;
- // RELIABLE COLLISION DETECTION:
- // Check if the bullet's path for this frame intersects the target's radius.
- // This prevents "tunneling" where a fast bullet could pass through a target between frames.
- var hitRadius = 85; // Visual radius of the chip, matches chip graphics
+ var hitRadius = 85;
var collisionDistance = distancePointToLine(self.target.x, self.target.y, prevX, prevY, newX, newY);
if (collisionDistance <= hitRadius) {
- // Hit detected
self.target.takeDamage(self.damage);
- // NEW: Check if this is a hearts bullet and flame mod is equipped
+ // Apply special effects based on mod
if (self.suit === 'hearts' && ModSystem.getEquippedModAsset('hearts') === 'burnHeartMod') {
- // Calculate burn damage (10% of hit damage per tick)
var burnDamage = self.damage * 0.1;
- // Increase burn duration by 50% (from 5 to 7.5 ticks)
- var burnDuration = Math.round(5 * 1.5); // 7.5 → 8 ticks = ~4 seconds of burning
- // Apply burn effect
+ var burnDuration = 8;
self.target.applyBurn(burnDamage, burnDuration);
}
- // NEW: Check if this is a spades bullet and freeze mod is equipped
if (self.suit === 'spades' && ModSystem.getEquippedModAsset('spades') === 'freezeSpadeMod') {
- // Calculate freeze duration (3 seconds base)
- var freezeDuration = 180; // 180 ticks at 60fps = 3 seconds
- // Apply freeze effect
- self.target.applyFreeze(freezeDuration);
+ self.target.applyFreeze(180);
}
- if (currentGraphic) {
- currentGraphic.visible = false;
- }
+ if (currentGraphic) currentGraphic.visible = false;
PoolManager.returnBullet(self);
} else {
- // No hit, move bullet forward
self.x = newX;
self.y = newY;
}
} else {
- // No target and not seeking, recycle
- if (currentGraphic) {
- currentGraphic.visible = false;
- }
+ if (currentGraphic) currentGraphic.visible = false;
PoolManager.returnBullet(self);
}
};
return self;
});
-// Helper function to calculate distance from point to line segment
/****
-* Object Pool Manager
-****/
-/****
* Card Class
****/
var Card = Container.expand(function (cardData) {
var self = Container.call(this);
self.cardData = cardData;
self.level = 1;
self.isInPlay = false;
- self.isPlayerCard = true; // Track which player owns this card
+ self.isPlayerCard = true;
self.playSlotX = 0;
self.playSlotY = 0;
self.lastFired = 0;
- self.fireRate = 60; // Base fire rate (ticks between shots)
- self.damage = 35; // Increased base damage
+ self.fireRate = 60;
+ self.damage = 35;
self.range = 200;
self.handBonus = 1;
- var redOutline = self.attachAsset('card', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- redOutline.scale.set(1.1);
- redOutline.alpha = 1.0;
- redOutline.tint = 0xff0000; // Red color
- redOutline.visible = false;
- self.redOutline = redOutline;
- var greenOutline = self.attachAsset('card', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- greenOutline.scale.set(1.1);
- greenOutline.alpha = 0.7;
- greenOutline.tint = 0x00ff00;
- greenOutline.visible = false;
- self.greenOutline = greenOutline;
+ // Outlines
+ var createOutline = function createOutline(color, alpha) {
+ var outline = self.attachAsset('card', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ outline.scale.set(1.1);
+ outline.alpha = alpha;
+ outline.tint = color;
+ outline.visible = false;
+ return outline;
+ };
+ self.redOutline = createOutline(0xff0000, 1.0);
+ self.greenOutline = createOutline(0x00ff00, 0.7);
+ // Card graphics
var cardGraphics = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
- // Define getSuitSymbol method before using it
- self.getSuitSymbol = function (suit) {
- switch (suit) {
- case 'hearts':
- return '♥';
- case 'diamonds':
- return '♦';
- case 'clubs':
- return '♣';
- case 'spades':
- return '♠';
- case 'joker':
- return '🃏';
- default:
- return '?';
- }
- };
- // Card value in top left corner
+ // Card value display
if (cardData.suit !== 'joker') {
var valueText = new Text2(cardData.value, {
size: 56,
fill: CardSystem.suitColors[cardData.suit] || 0x000000,
@@ -249,127 +174,86 @@
stroke: 0x000000,
strokeThickness: 0
});
valueText.anchor.set(0, 0);
- valueText.x = -95; // Top left
+ valueText.x = -95;
valueText.y = -135;
self.addChild(valueText);
}
- // Large suit symbol in center
- var suitAssetId = null;
- var equippedMod = ModSystem.getEquippedModAsset(cardData.suit);
- if (equippedMod) {
- suitAssetId = equippedMod;
- } else {
- switch (cardData.suit) {
- case 'hearts':
- suitAssetId = 'heartSuit';
- break;
- case 'diamonds':
- suitAssetId = 'diamondSuit';
- break;
- case 'clubs':
- suitAssetId = 'clubSuit';
- break;
- case 'spades':
- suitAssetId = 'spadeSuit';
- break;
- }
- }
+ // Suit graphics
+ var suitAssetMap = {
+ hearts: 'heartSuit',
+ diamonds: 'diamondSuit',
+ clubs: 'clubSuit',
+ spades: 'spadeSuit'
+ };
+ var suitAssetId = ModSystem.getEquippedModAsset(cardData.suit) || suitAssetMap[cardData.suit];
if (suitAssetId) {
var suitGraphics = self.attachAsset(suitAssetId, {
anchorX: 0.5,
anchorY: 0.5
});
suitGraphics.y = -15;
- suitGraphics.scaleX = suitGraphics.scaleY = 0.8;
+ suitGraphics.scale.set(0.8);
} else if (cardData.suit === 'joker') {
- var jokerSuitGraphics = self.attachAsset('jokerSuit', {
+ var jokerGraphics = self.attachAsset('jokerSuit', {
anchorX: 0.5,
anchorY: 0.5
});
- jokerSuitGraphics.y = -15;
- jokerSuitGraphics.scale.set(1.5);
+ jokerGraphics.y = -15;
+ jokerGraphics.scale.set(1.5);
}
- // Level text at bottom
+ // Level text
var levelText = new Text2('Lvl 1', {
size: 45,
fill: 0x000000,
weight: 800,
stroke: 0x000000,
strokeThickness: 0
});
levelText.anchor.set(0.5, 1);
- levelText.y = 128; // Bottom of card
+ levelText.y = 128;
self.addChild(levelText);
self.activate = function (x, y, inPlay, isPlayerCard) {
self.x = x;
self.y = y;
self.isInPlay = inPlay || false;
self.isPlayerCard = isPlayerCard !== undefined ? isPlayerCard : true;
self.visible = true;
- if (inPlay) {
- self.calculateStats();
- }
+ if (inPlay) self.calculateStats();
};
self.calculateStats = function () {
- // Stats based on level only, not card face value
- var baseDamage = 10; // Reduced base damage
+ var baseDamage = 10;
var baseFireRate = 60;
- // Level scaling - more dramatic improvements per level
- self.damage = Math.floor(baseDamage * Math.pow(1.6, self.level - 1)); // Further decreased scaling from 1.7 to 1.6
- self.fireRate = Math.max(15, Math.floor(baseFireRate / Math.pow(1.2, self.level - 1))); // Reduced from 1.25 to 1.2
- // Apply poker hand bonus
- self.damage = Math.floor(self.damage * self.handBonus);
- self.fireRate = Math.max(10, Math.floor(self.fireRate / self.handBonus));
+ self.damage = Math.floor(baseDamage * Math.pow(1.6, self.level - 1) * self.handBonus);
+ self.fireRate = Math.max(10, Math.floor(baseFireRate / (Math.pow(1.2, self.level - 1) * self.handBonus)));
};
self.setLevel = function (newLevel) {
if (self.cardData.suit === 'joker') {
self.level = 1;
levelText.visible = false;
- self.calculateStats();
- return;
+ } else {
+ self.level = newLevel;
+ levelText.setText('Lvl ' + self.level);
}
- self.level = newLevel;
- levelText.setText('Lvl ' + self.level);
self.calculateStats();
- // Visual feedback for higher levels
- if (self.level > 1) {
- // The glow effect was causing cards to become translucent.
- // Level up is already indicated by animation and floating text.
- }
};
self.canMergeWith = function (otherCard) {
- if (!otherCard || otherCard === self) {
- return false;
- }
- // If the card being dropped onto is a Joker, it cannot be leveled up.
- if (otherCard.cardData.suit === 'joker') {
- return false;
- }
- // If the card being dragged is a Joker, it can merge with any non-Joker card.
- if (self.cardData.suit === 'joker') {
- return true;
- }
- // Must be same level AND (same suit OR same value)
+ if (!otherCard || otherCard === self || otherCard.cardData.suit === 'joker') return false;
+ if (self.cardData.suit === 'joker') return true;
var sameLevel = self.level === otherCard.level;
var sameSuit = self.cardData.suit === otherCard.cardData.suit;
var sameValue = self.cardData.value === otherCard.cardData.value;
return sameLevel && (sameSuit || sameValue);
};
self.mergeWith = function (otherCard) {
- if (!self.canMergeWith(otherCard)) {
- return null;
- }
- // When merging, the new card levels up. The new level is one higher than the card on the board.
+ if (!self.canMergeWith(otherCard)) return null;
var newLevel = otherCard.level + 1;
- // Special case: If the card being dragged is a Joker, the target card just increases its level
if (self.cardData.suit === 'joker') {
var mergedCard = new Card(otherCard.cardData);
mergedCard.setLevel(newLevel);
return mergedCard;
}
- // The new card is of a random type, regardless of what was merged.
var randomSuit = CardSystem.suits[Math.floor(Math.random() * CardSystem.suits.length)];
var randomValue = CardSystem.values[Math.floor(Math.random() * CardSystem.values.length)];
var newCardData = {
suit: randomSuit,
@@ -380,106 +264,79 @@
mergedCard.setLevel(newLevel);
return mergedCard;
};
self.findTarget = function () {
- // Player cards target enemies attacking the player (activePlayerChips)
- // AI cards target enemies attacking the AI (activeAIChips)
var targets = self.isPlayerCard ? activePlayerChips : activeAIChips;
var bestTarget = null;
var highestProgress = -1;
- for (var i = 0; i < targets.length; i++) {
- var chip = targets[i];
- // Add extra validation for alive targets
+ targets.forEach(function (chip) {
if (chip.active && chip.health > 0 && chip.visible && chip.pathProgress > highestProgress) {
highestProgress = chip.pathProgress;
bestTarget = chip;
}
- }
+ });
return bestTarget;
};
self.fire = function () {
var target = self.findTarget();
- if (!target) {
- return;
- }
- // Check if this is a clubs card with spreadshot mod equipped
+ if (!target) return;
var isSpreadshot = self.cardData.suit === 'clubs' && ModSystem.getEquippedModAsset('clubs') === 'spreadClubMod';
if (isSpreadshot) {
- // Spreadshot: reduced damage but hits multiple targets
- var spreadDamage = Math.floor(self.damage * 0.6); // 30% damage reduction
- var maxTargets = Math.min(1 + self.level, 5); // 1 + level targets, max 5
- // Find multiple targets
+ var spreadDamage = Math.floor(self.damage * 0.6);
+ var maxTargets = Math.min(1 + self.level, 5);
var targets = self.isPlayerCard ? activePlayerChips : activeAIChips;
- var validTargets = [];
- // Get all valid targets and sort by distance
- for (var i = 0; i < targets.length; i++) {
- var chip = targets[i];
- if (chip.active && chip.health > 0 && chip.visible) {
- var dx = chip.x - self.x;
- var dy = chip.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- validTargets.push({
- chip: chip,
- distance: distance
- });
- }
- }
- // Sort by distance (closest first)
- validTargets.sort(function (a, b) {
+ var validTargets = targets.filter(function (chip) {
+ return chip.active && chip.health > 0 && chip.visible;
+ }).map(function (chip) {
+ return {
+ chip: chip,
+ distance: Math.sqrt(Math.pow(chip.x - self.x, 2) + Math.pow(chip.y - self.y, 2))
+ };
+ }).sort(function (a, b) {
return a.distance - b.distance;
- });
- // Fire at up to maxTargets
- var targetsHit = Math.min(maxTargets, validTargets.length);
- for (var i = 0; i < targetsHit; i++) {
+ }).slice(0, maxTargets);
+ validTargets.forEach(function (_ref) {
+ var chip = _ref.chip;
var bullet = PoolManager.getBullet();
if (bullet) {
- bullet.activate(self.x, self.y, validTargets[i].chip, spreadDamage, self.cardData.suit, self.isPlayerCard);
+ bullet.activate(self.x, self.y, chip, spreadDamage, self.cardData.suit, self.isPlayerCard);
gameLayer.addChild(bullet);
activeBullets.push(bullet);
}
- }
+ });
} else {
- // Normal single-target firing
var bullet = PoolManager.getBullet();
if (bullet) {
bullet.activate(self.x, self.y, target, self.damage, self.cardData.suit, self.isPlayerCard);
gameLayer.addChild(bullet);
activeBullets.push(bullet);
}
}
self.lastFired = LK.ticks;
- // Visual feedback for firing (enhanced for spreadshot)
+ // Fire animation
tween.stop(self, {
scaleX: true,
scaleY: true
});
- var scaleAmount = isSpreadshot ? 0.8 : 0.9; // More dramatic scale for spreadshot
+ var scaleAmount = isSpreadshot ? 0.8 : 0.9;
tween(self, {
scaleX: scaleAmount,
scaleY: scaleAmount
- }, {
- duration: 100,
- easing: tween.quadOut,
+ }, Object.assign({}, ANIMATION_CONFIGS.cardFire, {
onFinish: function onFinish() {
- tween(self, {
+ return tween(self, {
scaleX: 1,
scaleY: 1
- }, {
- duration: 150,
- easing: tween.elasticOut
- });
+ }, ANIMATION_CONFIGS.cardFireReturn);
}
- });
+ }));
};
self.update = function () {
- if (!self.isInPlay) {
- return;
- }
+ if (!self.isInPlay) return;
if (LK.ticks - self.lastFired >= self.fireRate) {
self.fire();
}
};
- // Initialize with level 1
self.setLevel(1);
return self;
});
/****
@@ -487,49 +344,39 @@
****/
var PokerChip = Container.expand(function () {
var self = Container.call(this);
self.active = false;
- self.health = 100; // Increased base health
+ self.health = 100;
self.maxHealth = 100;
self.value = 1;
self.speed = 0.05;
self.pathProgress = 0;
self.isPlayerSide = true;
self.damageFlashTimer = 0;
+ self.isBoss = false;
+ // Burn properties
self.burnDamage = 0;
self.burnDuration = 0;
self.burnTickTimer = 0;
- self.burnTickInterval = 30; // Burn ticks every 0.5 seconds (30 ticks at 60fps)
+ self.burnTickInterval = 30;
+ // Freeze properties
self.freezeDuration = 0;
self.originalSpeed = 0;
- self.iceCube = null; // Will hold the ice cube graphic
+ self.iceCube = null;
self.freezeImmunityTimer = 0;
- var chipGraphicsAssets = {
- 1: self.attachAsset('yellowChip', {
+ // Chip graphics
+ var chipValues = [1, 5, 10, 25, 100];
+ var chipColors = ['yellowChip', 'redChip', 'greenChip', 'blueChip', 'purpleChip'];
+ var chipGraphicsAssets = {};
+ var chipGraphics = null;
+ chipValues.forEach(function (value, index) {
+ var graphic = self.attachAsset(chipColors[index], {
anchorX: 0.5,
anchorY: 0.5
- }),
- 5: self.attachAsset('redChip', {
- anchorX: 0.5,
- anchorY: 0.5
- }),
- 10: self.attachAsset('greenChip', {
- anchorX: 0.5,
- anchorY: 0.5
- }),
- 25: self.attachAsset('blueChip', {
- anchorX: 0.5,
- anchorY: 0.5
- }),
- 100: self.attachAsset('purpleChip', {
- anchorX: 0.5,
- anchorY: 0.5
- })
- };
- var chipGraphics = null; // This will hold the current visible chip
- for (var val in chipGraphicsAssets) {
- chipGraphicsAssets[val].visible = false;
- }
+ });
+ graphic.visible = false;
+ chipGraphicsAssets[value] = graphic;
+ });
var healthText = new Text2('', {
size: 80,
fill: 0xffffff,
weight: 800,
@@ -538,45 +385,35 @@
});
healthText.anchor.set(0.5, 0.5);
self.addChild(healthText);
self.applyBurn = function (damage, duration) {
- // Stack burn damage but refresh duration
self.burnDamage += damage;
self.burnDuration = Math.max(self.burnDuration, duration);
self.burnTickTimer = 0;
- // REMOVED: Visual tint feedback - only use particles now
- // if (chipGraphics) {
- // chipGraphics.tint = 0xff4400; // Orange tint for burning
- // }
- // Create burn effect particles
- createBurnEffect(self.x, self.y);
+ AnimationHelper.createParticleEffect(self.x, self.y, 'burnHeartMod', {
+ count: 4,
+ floatUp: 50
+ });
};
self.applyFreeze = function (duration) {
- // Return if chip is immune to freeze
- if (self.freezeImmunityTimer > 0) {
- return;
- }
- // Don't freeze if already frozen - just refresh duration
+ if (self.freezeImmunityTimer > 0) return;
if (self.freezeDuration > 0) {
self.freezeDuration = Math.max(self.freezeDuration, duration);
return;
}
self.freezeDuration = duration;
- // Store original speed and stop movement
self.originalSpeed = self.speed;
self.speed = 0;
- // Create ice cube encasement
if (!self.iceCube) {
self.iceCube = LK.getAsset('iceCube', {
anchorX: 0.5,
anchorY: 0.5
});
- self.iceCube.scale.set(1.2); // Slightly larger than chip
+ self.iceCube.scale.set(1.2);
self.iceCube.alpha = 0.8;
self.addChild(self.iceCube);
}
self.iceCube.visible = true;
- // Ice formation animation
self.iceCube.scale.set(0.1);
self.iceCube.alpha = 0;
tween(self.iceCube, {
scaleX: 1.2,
@@ -587,157 +424,116 @@
easing: tween.backOut
});
};
self.processFreeze = function () {
- // Process immunity first, which ticks down every frame.
- if (self.freezeImmunityTimer > 0) {
- self.freezeImmunityTimer--;
- }
- if (self.freezeDuration <= 0) {
- return;
- }
+ if (self.freezeImmunityTimer > 0) self.freezeImmunityTimer--;
+ if (self.freezeDuration <= 0) return;
self.freezeDuration--;
- // Create occasional ice sparkle effects
if (Math.random() < 0.1) {
- createIceSparkles(self.x, self.y);
+ AnimationHelper.createParticleEffect(self.x, self.y, 'iceCube', {
+ count: 2,
+ scale: {
+ min: 0.1,
+ max: 0.2
+ },
+ alpha: 0.6,
+ tint: 0x88ddff,
+ floatUp: 20
+ });
}
- // When freeze expires, break the ice
if (self.freezeDuration <= 0) {
self.speed = self.originalSpeed;
- self.freezeImmunityTimer = 120; // Apply 2-second immunity (120 ticks)
+ self.freezeImmunityTimer = 120;
if (self.iceCube) {
- // Ice breaking animation
tween(self.iceCube, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
easing: tween.quadOut,
onFinish: function onFinish() {
- self.iceCube.visible = false;
+ return self.iceCube.visible = false;
}
});
- // Create ice shatter particles
- createIceShatterEffect(self.x, self.y);
+ AnimationHelper.createParticleEffect(self.x, self.y, 'iceCube', {
+ count: 6,
+ tint: 0xaaeeff,
+ rotation: Math.PI * 2
+ });
}
}
};
self.processBurnDamage = function () {
if (self.burnDuration <= 0) {
- // Ensure burn is completely cleared
self.burnDamage = 0;
self.burnTickTimer = 0;
return;
}
self.burnTickTimer++;
if (self.burnTickTimer >= self.burnTickInterval) {
self.burnTickTimer = 0;
self.burnDuration--;
- // Apply burn damage (10% of original hit damage)
var actualBurnDamage = Math.ceil(self.burnDamage);
self.health -= actualBurnDamage;
self.updateHealthText();
- // Create floating burn damage text
createFloatingText('-' + actualBurnDamage, self.x + (Math.random() - 0.5) * 40, self.y - 30, 0xff4400, 30);
- // Burn particles
- createBurnEffect(self.x, self.y);
+ AnimationHelper.createParticleEffect(self.x, self.y, 'burnHeartMod', {
+ count: 4,
+ floatUp: 50
+ });
if (self.health <= 0) {
self.active = false;
self.die();
return;
}
- // Reduce burn damage over time (burn weakens)
self.burnDamage *= 0.9;
- // Clear burn when duration expires - NO TINT MANAGEMENT
if (self.burnDuration <= 0) {
self.burnDamage = 0;
- self.burnTickTimer = 0; // Also reset tick timer
- // REMOVED: No tint reset needed
- // if (chipGraphics && self.damageFlashTimer <= 0) {
- // chipGraphics.tint = 0xffffff;
- // }
+ self.burnTickTimer = 0;
}
}
};
self.activate = function (value, isPlayerSide, startPos) {
self.active = true;
self.visible = true;
self.value = value;
self.isPlayerSide = isPlayerSide;
- // Mark boss chips (they have much higher values than normal chips)
self.isBoss = value >= 100;
- // Health scales with chip value - more valuable chips are tankier
+ // Calculate health
self.maxHealth = value * 50;
- // Double health after every 10 waves
var healthMultiplier = Math.pow(2, Math.floor(WaveSystem.waveNumber / 10));
- self.maxHealth = self.maxHealth * healthMultiplier;
+ self.maxHealth *= healthMultiplier;
self.health = self.maxHealth;
+ // Reset all states
self.pathProgress = 0;
- // NEW: Reset burn status to ensure clean state
self.burnDamage = 0;
self.burnDuration = 0;
self.burnTickTimer = 0;
- // NEW: Reset freeze status to ensure clean state
self.freezeDuration = 0;
self.originalSpeed = 0;
- if (self.iceCube) {
- self.iceCube.visible = false;
- }
self.freezeImmunityTimer = 0;
- // Set all chips to yellow chip speed
- self.speed = 0.03;
- // Reset damage flash timer
self.damageFlashTimer = 0;
- // Recreate healthText to ensure proper styling
- if (healthText && healthText.parent) {
- healthText.parent.removeChild(healthText);
- }
- healthText = new Text2('', {
- size: 80,
- fill: 0xffffff,
- weight: 800,
- stroke: 0x000000,
- strokeThickness: 8
- });
- healthText.anchor.set(0.5, 0.5);
- self.addChild(healthText);
+ self.speed = 0.03;
+ if (self.iceCube) self.iceCube.visible = false;
self.setChipAppearance();
self.x = startPos.x;
self.y = startPos.y;
};
self.updateHealthText = function () {
- // Remove the old text object
- if (healthText && healthText.parent) {
- healthText.parent.removeChild(healthText);
- }
- // Create a new text object with all styling properties
- healthText = new Text2(formatNumberWithSuffix(Math.max(0, self.health)), {
- size: 80,
- fill: 0xffffff,
- weight: 800,
- stroke: 0x000000,
- strokeThickness: 8
- });
- healthText.anchor.set(0.5, 0.5);
- self.addChild(healthText);
+ healthText.setText(formatNumberWithSuffix(Math.max(0, self.health)));
};
self.setChipAppearance = function () {
- if (chipGraphics) {
- chipGraphics.visible = false;
- }
+ if (chipGraphics) chipGraphics.visible = false;
chipGraphics = chipGraphicsAssets[self.value] || chipGraphicsAssets[1];
- if (chipGraphics) {
- chipGraphics.visible = true;
- }
+ if (chipGraphics) chipGraphics.visible = true;
self.updateHealthText();
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthText();
- self.damageFlashTimer = 10; // Flash for 10 ticks instead of setTimeout
+ self.damageFlashTimer = 10;
if (self.health <= 0) {
- // IMMEDIATELY mark as inactive to prevent further hits
self.active = false;
self.die();
}
};
@@ -747,39 +543,31 @@
var greedBonus = calculateGreedBonus(true, chipsEarned);
var totalEarned = chipsEarned + greedBonus;
gameState.playerChips += totalEarned;
if (greedBonus > 0) {
- createFloatingText('+' + formatNumberWithSuffix(totalEarned) + ' (+' + formatNumberWithSuffix(greedBonus) + ' greed)', self.x, self.y - 30, 0xffd700, 35);
+ createFloatingText("+".concat(formatNumberWithSuffix(totalEarned), " (+").concat(formatNumberWithSuffix(greedBonus), " greed)"), self.x, self.y - 30, 0xffd700, 35);
}
} else {
var greedBonus = calculateGreedBonus(false, chipsEarned);
gameState.aiChips += chipsEarned + greedBonus;
}
PoolManager.returnChip(self);
};
self.update = function () {
- if (!self.active) {
- return;
- }
- // Handle damage flash (keep this for regular damage)
+ if (!self.active) return;
+ // Handle damage flash
if (self.damageFlashTimer > 0) {
self.damageFlashTimer--;
if (chipGraphics) {
- // SIMPLIFIED: Only red flash for damage, no burn tint management
chipGraphics.tint = self.damageFlashTimer > 0 ? 0xff0000 : 0xffffff;
}
}
- // NEW: Process burn damage
self.processBurnDamage();
- // NEW: Process freeze effect
self.processFreeze();
- if (!self.active) {
- return;
- }
+ if (!self.active) return;
self.pathProgress += self.speed;
var pathPos = PathSystem.getPositionAlongPath(self.pathProgress, self.isPlayerSide);
if (pathPos.completed) {
- // Boss chips remove 2 hearts instead of 1
var heartsToRemove = self.isBoss ? 2 : 1;
if (self.isPlayerSide) {
gameState.playerLives -= heartsToRemove;
} else {
@@ -803,42 +591,14 @@
/****
* Game Code
****/
-// Helper function to calculate distance from point to line segment
/****
-* Poker Tower Defense - Complete Refactor
+* Constants
****/
/****
-* Game Constants
+* Card System
****/
-function distancePointToLine(px, py, x1, y1, x2, y2) {
- var A = px - x1;
- var B = py - y1;
- var C = x2 - x1;
- var D = y2 - y1;
- var dot = A * C + B * D;
- var lenSq = C * C + D * D;
- if (lenSq === 0) {
- // Line segment is actually a point
- return Math.sqrt(A * A + B * B);
- }
- var param = dot / lenSq;
- var xx, yy;
- if (param < 0) {
- xx = x1;
- yy = y1;
- } else if (param > 1) {
- xx = x2;
- yy = y2;
- } else {
- xx = x1 + param * C;
- yy = y1 + param * D;
- }
- var dx = px - xx;
- var dy = py - yy;
- return Math.sqrt(dx * dx + dy * dy);
-}
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
@@ -880,29 +640,35 @@
f: function f() {
try {
a || null == t["return"] || t["return"]();
} finally {
- if (u) {
- throw o;
- }
+ if (u) throw o;
}
}
};
}
+function _toConsumableArray(r) {
+ return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
+}
+function _nonIterableSpread() {
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
+}
function _unsupportedIterableToArray(r, a) {
if (r) {
- if ("string" == typeof r) {
- return _arrayLikeToArray(r, a);
- }
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
+function _iterableToArray(r) {
+ if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
+}
+function _arrayWithoutHoles(r) {
+ if (Array.isArray(r)) return _arrayLikeToArray(r);
+}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
- for (var e = 0, n = Array(a); e < a; e++) {
- n[e] = r[e];
- }
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
var SCREEN_WIDTH = 2048;
var SCREEN_HEIGHT = 2732;
@@ -911,19 +677,147 @@
var SLOT_WIDTH = 300;
var SLOT_HEIGHT = 420;
var DEAL_SLOT_WIDTH = 240;
var DEAL_SLOT_HEIGHT = 330;
-// AI area positioning (top)
+var HAND_GAP = 30;
+var HAND_WIDTH = 5 * DEAL_SLOT_WIDTH + 4 * HAND_GAP;
+var HAND_START_X = (SCREEN_WIDTH - HAND_WIDTH) / 2;
+// Area positioning
var AI_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2;
var AI_AREA_Y = 150;
-// Player area positioning (middle, with plenty of room below)
var PLAYER_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2;
-var PLAYER_AREA_Y = SCREEN_HEIGHT - 1300; // Much higher up
-// Player deal area (hand slots) - below play area
+var PLAYER_AREA_Y = SCREEN_HEIGHT - 1300;
var PLAYER_DEAL_AREA_Y = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + 100;
+// Common animation configs
+var ANIMATION_CONFIGS = {
+ cardFire: {
+ duration: 100,
+ easing: tween.quadOut
+ },
+ cardFireReturn: {
+ duration: 150,
+ easing: tween.elasticOut
+ },
+ cardMerge: {
+ duration: 300,
+ easing: tween.elasticOut
+ },
+ cardDeal: {
+ duration: 250,
+ easing: tween.cubicOut
+ },
+ cardSettle: {
+ duration: 150,
+ easing: tween.quadIn
+ },
+ particleFade: {
+ duration: 600,
+ easing: tween.quadOut
+ }
+};
/****
-* Card System
+* Utility Functions
****/
+function formatNumberWithSuffix(number) {
+ var num = Math.round(number);
+ var suffixes = [{
+ value: 1e12,
+ suffix: 'T'
+ }, {
+ value: 1e9,
+ suffix: 'B'
+ }, {
+ value: 1e6,
+ suffix: 'M'
+ }, {
+ value: 1e3,
+ suffix: 'K'
+ }];
+ for (var _i = 0, _suffixes = suffixes; _i < _suffixes.length; _i++) {
+ var _suffixes$_i = _suffixes[_i],
+ value = _suffixes$_i.value,
+ suffix = _suffixes$_i.suffix;
+ if (num >= value) {
+ return (num / value).toFixed(1).replace(/\.0$/, '') + suffix;
+ }
+ }
+ return num.toString();
+}
+function distancePointToLine(px, py, x1, y1, x2, y2) {
+ var A = px - x1,
+ B = py - y1;
+ var C = x2 - x1,
+ D = y2 - y1;
+ var lenSq = C * C + D * D;
+ if (lenSq === 0) return Math.sqrt(A * A + B * B);
+ var param = Math.max(0, Math.min(1, (A * C + B * D) / lenSq));
+ var xx = x1 + param * C;
+ var yy = y1 + param * D;
+ return Math.sqrt((px - xx) * (px - xx) + (py - yy) * (py - yy));
+}
+function getHandSlotPosition(index) {
+ return {
+ x: HAND_START_X + index * (DEAL_SLOT_WIDTH + HAND_GAP) + DEAL_SLOT_WIDTH / 2,
+ y: PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2
+ };
+}
+/****
+* Animation Helpers
+****/
+var AnimationHelper = {
+ createParticleEffect: function createParticleEffect(x, y, particleAsset, config) {
+ var defaults = {
+ count: 4,
+ spread: 80,
+ scale: {
+ min: 0.3,
+ max: 0.5
+ },
+ distance: {
+ min: 25,
+ max: 65
+ },
+ duration: {
+ min: 400,
+ max: 900
+ },
+ tint: 0xffffff,
+ alpha: 1
+ };
+ var settings = Object.assign({}, defaults, config);
+ for (var i = 0; i < settings.count; i++) {
+ var particle = LK.getAsset(particleAsset, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ particle.x = x + (Math.random() - 0.5) * settings.spread;
+ particle.y = y + (Math.random() - 0.5) * settings.spread;
+ particle.scale.set(settings.scale.min + Math.random() * (settings.scale.max - settings.scale.min));
+ particle.tint = settings.tint;
+ particle.alpha = settings.alpha;
+ var angle = Math.random() * Math.PI * 2;
+ var distance = settings.distance.min + Math.random() * (settings.distance.max - settings.distance.min);
+ var duration = settings.duration.min + Math.random() * (settings.duration.max - settings.duration.min);
+ var targetX = x + Math.cos(angle) * distance;
+ var targetY = y + Math.sin(angle) * distance - (settings.floatUp || 0);
+ gameLayer.addChild(particle);
+ tween(particle, {
+ x: targetX,
+ y: targetY,
+ alpha: 0,
+ scaleX: settings.endScale || 0.1,
+ scaleY: settings.endScale || 0.1,
+ rotation: settings.rotation || 0
+ }, {
+ duration: duration,
+ easing: tween.quadOut,
+ onFinish: function onFinish() {
+ return particle.parent && particle.parent.removeChild(particle);
+ }
+ });
+ }
+ }
+};
var CardSystem = {
suits: ['hearts', 'diamonds', 'clubs', 'spades'],
values: ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'],
suitColors: {
@@ -932,37 +826,19 @@
'clubs': 0x000000,
'spades': 0x000000
},
createDeck: function createDeck() {
+ var _this = this;
var deck = [];
- var _iterator = _createForOfIteratorHelper(this.suits),
- _step;
- try {
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
- var suit = _step.value;
- var _iterator2 = _createForOfIteratorHelper(this.values),
- _step2;
- try {
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
- var value = _step2.value;
- deck.push({
- suit: suit,
- value: value,
- id: suit + '_' + value
- });
- }
- } catch (err) {
- _iterator2.e(err);
- } finally {
- _iterator2.f();
- }
- }
- // Add jokers
- } catch (err) {
- _iterator.e(err);
- } finally {
- _iterator.f();
- }
+ this.suits.forEach(function (suit) {
+ _this.values.forEach(function (value) {
+ deck.push({
+ suit: suit,
+ value: value,
+ id: "".concat(suit, "_").concat(value)
+ });
+ });
+ });
deck.push({
suit: 'joker',
value: 'red',
id: 'joker_red'
@@ -976,53 +852,43 @@
},
shuffleDeck: function shuffleDeck(deck) {
for (var i = deck.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
- var temp = deck[i];
- deck[i] = deck[j];
- deck[j] = temp;
+ var _ref2 = [deck[j], deck[i]];
+ deck[i] = _ref2[0];
+ deck[j] = _ref2[1];
}
return deck;
},
getCardValue: function getCardValue(card) {
- if (card.cardData.suit === 'joker') {
- return 14;
- } // Jokers are highest
- if (card.cardData.value === 'A') {
- return 14;
- } // Aces high
- if (card.cardData.value === 'K') {
- return 13;
- }
- if (card.cardData.value === 'Q') {
- return 12;
- }
- if (card.cardData.value === 'J') {
- return 11;
- }
- return parseInt(card.cardData.value);
+ var valueMap = {
+ 'A': 14,
+ 'K': 13,
+ 'Q': 12,
+ 'J': 11
+ };
+ if (card.cardData.suit === 'joker') return 14;
+ return valueMap[card.cardData.value] || parseInt(card.cardData.value);
},
evaluatePokerHand: function evaluatePokerHand(cards) {
- var _this = this;
+ var _this2 = this;
if (!cards || cards.length === 0) {
return {
type: 'none',
strength: 0,
multiplier: 1,
contributingCards: []
};
}
- // Sort cards by value for easier analysis
- var sortedCards = cards.slice().sort(function (a, b) {
- return _this.getCardValue(b) - _this.getCardValue(a);
+ var sortedCards = _toConsumableArray(cards).sort(function (a, b) {
+ return _this2.getCardValue(b) - _this2.getCardValue(a);
});
var values = sortedCards.map(function (card) {
- return _this.getCardValue(card);
+ return _this2.getCardValue(card);
});
var suits = sortedCards.map(function (card) {
return card.cardData.suit;
});
- // Count values and suits
var valueCounts = {};
var suitCounts = {};
values.forEach(function (value) {
return valueCounts[value] = (valueCounts[value] || 0) + 1;
@@ -1032,152 +898,154 @@
});
var counts = Object.values(valueCounts).sort(function (a, b) {
return b - a;
});
- // Only check for 5-card hands if we have 5 cards
- var isFlush = false;
- var isStraight = false;
- if (cards.length === 5) {
- isFlush = Object.keys(suitCounts).length === 1;
- isStraight = this.checkStraight(values);
- }
- // Royal Flush
- if (isFlush && isStraight && values[0] === 14 && values[4] === 10) {
- return {
+ var isFlush = cards.length === 5 && Object.keys(suitCounts).length === 1;
+ var isStraight = cards.length === 5 && this.checkStraight(values);
+ // Check hand types in order of strength
+ var handChecks = [{
+ check: function check() {
+ return isFlush && isStraight && values[0] === 14 && values[4] === 10;
+ },
+ result: {
type: 'royal_flush',
strength: 10,
multiplier: 25,
contributingCards: sortedCards
- };
- }
- // Straight Flush
- if (isFlush && isStraight) {
- return {
+ }
+ }, {
+ check: function check() {
+ return isFlush && isStraight;
+ },
+ result: {
type: 'straight_flush',
strength: 9,
multiplier: 12,
contributingCards: sortedCards
- };
- }
- // Four of a Kind
- if (counts[0] >= 4) {
- var quadValue;
- for (var v in valueCounts) {
- if (valueCounts[v] >= 4) {
- quadValue = parseInt(v);
- break;
- }
}
- return {
- type: 'four_of_a_kind',
- strength: 8,
- multiplier: 8,
- contributingCards: sortedCards.filter(function (c) {
- return _this.getCardValue(c) === quadValue;
- })
- };
- }
- // Full House
- if (counts[0] === 3 && counts[1] === 2) {
- return {
+ }, {
+ check: function check() {
+ return counts[0] >= 4;
+ },
+ result: function result() {
+ var quadValue = parseInt(Object.keys(valueCounts).find(function (v) {
+ return valueCounts[v] >= 4;
+ }));
+ return {
+ type: 'four_of_a_kind',
+ strength: 8,
+ multiplier: 8,
+ contributingCards: sortedCards.filter(function (c) {
+ return _this2.getCardValue(c) === quadValue;
+ })
+ };
+ }
+ }, {
+ check: function check() {
+ return counts[0] === 3 && counts[1] === 2;
+ },
+ result: {
type: 'full_house',
strength: 7,
multiplier: 5,
contributingCards: sortedCards
- };
- }
- // Flush
- if (isFlush) {
- return {
+ }
+ }, {
+ check: function check() {
+ return isFlush;
+ },
+ result: {
type: 'flush',
strength: 6,
multiplier: 3.5,
contributingCards: sortedCards
- };
- }
- // Straight
- if (isStraight) {
- return {
+ }
+ }, {
+ check: function check() {
+ return isStraight;
+ },
+ result: {
type: 'straight',
strength: 5,
multiplier: 2.5,
contributingCards: sortedCards
- };
- }
- // Three of a Kind
- if (counts[0] === 3) {
- var tripValue;
- for (var v in valueCounts) {
- if (valueCounts[v] === 3) {
- tripValue = parseInt(v);
- break;
- }
}
- return {
- type: 'three_of_a_kind',
- strength: 4,
- multiplier: 2,
- contributingCards: sortedCards.filter(function (c) {
- return _this.getCardValue(c) === tripValue;
- })
- };
- }
- // Two Pair
- if (counts[0] === 2 && counts[1] === 2) {
- var pairValues = [];
- for (var v in valueCounts) {
- if (valueCounts[v] === 2) {
- pairValues.push(parseInt(v));
- }
+ }, {
+ check: function check() {
+ return counts[0] === 3;
+ },
+ result: function result() {
+ var tripValue = parseInt(Object.keys(valueCounts).find(function (v) {
+ return valueCounts[v] === 3;
+ }));
+ return {
+ type: 'three_of_a_kind',
+ strength: 4,
+ multiplier: 2,
+ contributingCards: sortedCards.filter(function (c) {
+ return _this2.getCardValue(c) === tripValue;
+ })
+ };
}
- return {
- type: 'two_pair',
- strength: 3,
- multiplier: 1.5,
- contributingCards: sortedCards.filter(function (c) {
- return pairValues.indexOf(_this.getCardValue(c)) !== -1;
- })
- };
- }
- // One Pair
- if (counts[0] === 2) {
- var pairValue;
- for (var v in valueCounts) {
- if (valueCounts[v] === 2) {
- pairValue = parseInt(v);
- break;
- }
+ }, {
+ check: function check() {
+ return counts[0] === 2 && counts[1] === 2;
+ },
+ result: function result() {
+ var pairValues = Object.keys(valueCounts).filter(function (v) {
+ return valueCounts[v] === 2;
+ }).map(function (v) {
+ return parseInt(v);
+ });
+ return {
+ type: 'two_pair',
+ strength: 3,
+ multiplier: 1.5,
+ contributingCards: sortedCards.filter(function (c) {
+ return pairValues.includes(_this2.getCardValue(c));
+ })
+ };
}
- return {
- type: 'one_pair',
- strength: 2,
- multiplier: 1.2,
- contributingCards: sortedCards.filter(function (c) {
- return _this.getCardValue(c) === pairValue;
- })
- };
+ }, {
+ check: function check() {
+ return counts[0] === 2;
+ },
+ result: function result() {
+ var pairValue = parseInt(Object.keys(valueCounts).find(function (v) {
+ return valueCounts[v] === 2;
+ }));
+ return {
+ type: 'one_pair',
+ strength: 2,
+ multiplier: 1.2,
+ contributingCards: sortedCards.filter(function (c) {
+ return _this2.getCardValue(c) === pairValue;
+ })
+ };
+ }
+ }];
+ for (var _i2 = 0, _handChecks = handChecks; _i2 < _handChecks.length; _i2++) {
+ var _handChecks$_i = _handChecks[_i2],
+ check = _handChecks$_i.check,
+ result = _handChecks$_i.result;
+ if (check()) {
+ return typeof result === 'function' ? result() : result;
+ }
}
- // High Card
return {
type: 'high_card',
strength: 1,
multiplier: 1,
contributingCards: [sortedCards[0]]
};
},
checkStraight: function checkStraight(values) {
- if (values.length !== 5) {
- return false;
- }
- // Check for ace-low straight (A, 2, 3, 4, 5)
- if (values[0] === 14 && values[1] === 5 && values[2] === 4 && values[3] === 3 && values[4] === 2) {
- return true;
- }
+ if (values.length !== 5) return false;
+ // Check ace-low straight
+ if (values[0] === 14 && values[1] === 5 && values[2] === 4 && values[3] === 3 && values[4] === 2) return true;
// Check normal straight
for (var i = 0; i < 4; i++) {
- if (values[i] - values[i + 1] !== 1) {
- return false;
- }
+ if (values[i] - values[i + 1] !== 1) return false;
}
return true;
}
};
@@ -1186,14 +1054,11 @@
****/
var PoolManager = {
chipPool: [],
bulletPool: [],
- cardPool: [],
CHIP_POOL_SIZE: 50,
BULLET_POOL_SIZE: 100,
- CARD_POOL_SIZE: 60,
init: function init() {
- // Initialize pools
for (var i = 0; i < this.CHIP_POOL_SIZE; i++) {
var chip = new PokerChip();
chip.active = false;
chip.visible = false;
@@ -1206,173 +1071,141 @@
this.bulletPool.push(bullet);
}
},
getChip: function getChip() {
- for (var i = 0; i < this.chipPool.length; i++) {
- if (!this.chipPool[i].active) {
- return this.chipPool[i];
- }
- }
- return null;
+ return this.chipPool.find(function (chip) {
+ return !chip.active;
+ }) || null;
},
getBullet: function getBullet() {
- for (var i = 0; i < this.bulletPool.length; i++) {
- if (!this.bulletPool[i].active) {
- return this.bulletPool[i];
- }
- }
- return null;
+ return this.bulletPool.find(function (bullet) {
+ return !bullet.active;
+ }) || null;
},
returnChip: function returnChip(chip) {
- // Force-set inactive state
chip.active = false;
chip.visible = false;
- chip.health = 0; // Ensure health is 0
- // Reset tint on all visual components of the chip.
- // This is to ensure that chip graphics don't retain the red damage flash tint
- // when being reused from the pool.
- if (chip.children) {
- for (var i = 0; i < chip.children.length; i++) {
- var child = chip.children[i];
- if (child.tint !== undefined) {
- child.tint = 0xffffff;
- }
- }
- }
- // NEW: Clear burn status completely
+ chip.health = 0;
+ // Reset all visual components
+ chip.children.forEach(function (child) {
+ if (child.tint !== undefined) child.tint = 0xffffff;
+ });
+ // Clear burn/freeze states
chip.burnDamage = 0;
chip.burnDuration = 0;
chip.burnTickTimer = 0;
- // Clear ownership flag
- var wasPlayerSide = chip.isPlayerSide;
- chip.isPlayerSide = null;
- // Remove from active arrays (check both arrays to be safe)
- var playerIndex = activePlayerChips.indexOf(chip);
- if (playerIndex !== -1) {
- activePlayerChips.splice(playerIndex, 1);
- }
- var aiIndex = activeAIChips.indexOf(chip);
- if (aiIndex !== -1) {
- activeAIChips.splice(aiIndex, 1);
- }
+ chip.freezeDuration = 0;
+ chip.freezeImmunityTimer = 0;
+ // Remove from active arrays
+ activePlayerChips = activePlayerChips.filter(function (c) {
+ return c !== chip;
+ });
+ activeAIChips = activeAIChips.filter(function (c) {
+ return c !== chip;
+ });
// Remove from containers
- if (chip.parent) {
- chip.parent.removeChild(chip);
- }
- // Double-check removal from both containers
- if (activePlayerChipsContainer.children.indexOf(chip) !== -1) {
- activePlayerChipsContainer.removeChild(chip);
- }
- if (activeAIChipsContainer.children.indexOf(chip) !== -1) {
- activeAIChipsContainer.removeChild(chip);
- }
+ if (chip.parent) chip.parent.removeChild(chip);
},
returnBullet: function returnBullet(bullet) {
- var explosionColor = 0x333333; // Dark Grey for clubs/spades
- if (bullet.suit === 'hearts' || bullet.suit === 'diamonds') {
- explosionColor = 0xff0000; // Red for hearts/diamonds
- }
- createExplosion(bullet.x, bullet.y, explosionColor);
+ var explosionColor = bullet.suit === 'hearts' || bullet.suit === 'diamonds' ? 0xff0000 : 0x333333;
+ AnimationHelper.createParticleEffect(bullet.x, bullet.y, 'explosionParticle', {
+ count: 5,
+ tint: explosionColor,
+ scale: {
+ min: 0.5,
+ max: 1
+ },
+ distance: {
+ min: 20,
+ max: 70
+ }
+ });
bullet.active = false;
bullet.visible = false;
- bullet.target = null; // Add this line to clear the target reference
- var index = activeBullets.indexOf(bullet);
- if (index !== -1) {
- activeBullets.splice(index, 1);
- }
- gameLayer.removeChild(bullet);
+ bullet.target = null;
+ activeBullets = activeBullets.filter(function (b) {
+ return b !== bullet;
+ });
+ if (bullet.parent) bullet.parent.removeChild(bullet);
}
};
/****
* Path System
****/
var PathSystem = {
playerPath: [],
aiPath: [],
+ playerPathLength: 0,
+ aiPathLength: 0,
init: function init() {
- // Create player path - rectangular loop around the play area
- var padding = 130; // Distance from play area - increased by 50 pixels
- var verticalPadding = padding - 25 - 30; // Reduced by 25 + 30 to decrease side length by 110px total
+ var padding = 130;
+ var verticalPadding = padding - 55;
+ // Player path
var leftX = PLAYER_AREA_X - padding + 10;
- var rightX = PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding - 20 - 10;
+ var rightX = PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding - 30;
var topY = PLAYER_AREA_Y - verticalPadding;
var bottomY = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + verticalPadding;
- this.playerPath = [
- // Start at bottom left
- {
+ this.playerPath = [{
x: leftX,
y: bottomY
- },
- // Go up the left side
- {
+ }, {
x: leftX,
y: topY
- },
- // Go across the top
- {
+ }, {
x: rightX,
y: topY
- },
- // Go down the right side
- {
+ }, {
x: rightX,
y: bottomY
}];
- // Create AI path - UPSIDE DOWN mirror of player path
+ // AI path (upside down mirror)
var aiLeftX = AI_AREA_X - padding;
var aiRightX = AI_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding;
- var aiVerticalPadding = verticalPadding - 40 + 15; // Additional 40px reduction on each side for AI (80px total), extended by 15px
+ var aiVerticalPadding = verticalPadding - 25;
var aiTopY = AI_AREA_Y - aiVerticalPadding;
var aiBottomY = AI_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + aiVerticalPadding;
- this.aiPath = [
- // Start at TOP left (opposite of player)
- {
+ this.aiPath = [{
x: aiLeftX,
y: aiTopY
- },
- // Go DOWN the left side (opposite of player)
- {
+ }, {
x: aiLeftX,
y: aiBottomY
- },
- // Go across the BOTTOM (opposite of player)
- {
+ }, {
x: aiRightX,
y: aiBottomY
- },
- // Go UP the right side (opposite of player)
- {
+ }, {
x: aiRightX,
y: aiTopY
}];
+ // Cache path lengths
+ this.playerPathLength = this.calculatePathLength(this.playerPath);
+ this.aiPathLength = this.calculatePathLength(this.aiPath);
},
- // Alternative offset method that spreads chips along the path direction:
getPathStart: function getPathStart(isPlayerSide) {
- var baseStart = isPlayerSide ? this.playerPath[0] : this.aiPath[0];
- var pathDirection = isPlayerSide ? this.playerPath[1] : this.aiPath[1];
- // Calculate direction vector from start to next point
+ var path = isPlayerSide ? this.playerPath : this.aiPath;
+ var baseStart = path[0];
+ var pathDirection = path[1];
var dx = pathDirection.x - baseStart.x;
var dy = pathDirection.y - baseStart.y;
var length = Math.sqrt(dx * dx + dy * dy);
- // Normalize direction vector
var normalizedX = dx / length;
var normalizedY = dy / length;
- // Random offset along the path (backward from start point)
- var pathOffset = Math.random() * 80; // 0-80 pixels back along path
- var sideOffset = (Math.random() - 0.5) * 40; // -20 to +20 pixels to the side
+ var pathOffset = Math.random() * 80;
+ var sideOffset = (Math.random() - 0.5) * 40;
return {
x: baseStart.x - normalizedX * pathOffset + normalizedY * sideOffset,
y: baseStart.y - normalizedY * pathOffset - normalizedX * sideOffset
};
},
getPositionAlongPath: function getPositionAlongPath(progress, isPlayerSide) {
var path = isPlayerSide ? this.playerPath : this.aiPath;
- var pathLength = this.calculatePathLength(path);
+ var pathLength = isPlayerSide ? this.playerPathLength : this.aiPathLength;
var targetDistance = progress / 100 * pathLength;
if (targetDistance >= pathLength) {
+ var lastPoint = path[path.length - 1];
return {
- x: path[path.length - 1].x,
- y: path[path.length - 1].y,
+ x: lastPoint.x,
+ y: lastPoint.y,
completed: true
};
}
var currentDistance = 0;
@@ -1387,11 +1220,12 @@
};
}
currentDistance += segmentLength;
}
+ var lastPoint = path[path.length - 1];
return {
- x: path[path.length - 1].x,
- y: path[path.length - 1].y,
+ x: lastPoint.x,
+ y: lastPoint.y,
completed: true
};
},
calculatePathLength: function calculatePathLength(path) {
@@ -1412,106 +1246,63 @@
****/
var ChipSpawner = {
spawnChip: function spawnChip(value, isPlayerSide) {
var chip = PoolManager.getChip();
- if (!chip) {
- return;
- }
+ if (!chip) return;
var activeChips = isPlayerSide ? activePlayerChips : activeAIChips;
- var minDistance = 85; // Half the chip diameter (170/2) to allow controlled overlap
- var newPos;
- // If no other chips exist, we can place it anywhere.
- if (activeChips.length === 0) {
- newPos = PathSystem.getPathStart(isPlayerSide);
- } else {
- var maxAttempts = 25; // Try to find a free spot with random placement first. It's fast.
+ var minDistance = 85;
+ var newPos = PathSystem.getPathStart(isPlayerSide);
+ if (activeChips.length > 0) {
var foundPosition = false;
- for (var attempt = 0; attempt < maxAttempts; attempt++) {
+ // Try random positions first
+ for (var attempt = 0; attempt < 25; attempt++) {
var candidatePos = PathSystem.getPathStart(isPlayerSide);
- var isValid = true;
- for (var i = 0; i < activeChips.length; i++) {
- var otherChip = activeChips[i];
+ var isValid = activeChips.every(function (otherChip) {
var dx = candidatePos.x - otherChip.x;
var dy = candidatePos.y - otherChip.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < minDistance) {
- isValid = false;
- break;
- }
- }
+ return Math.sqrt(dx * dx + dy * dy) >= minDistance;
+ });
if (isValid) {
newPos = candidatePos;
foundPosition = true;
break;
}
}
- // If random placements failed, use a more deterministic spiral search.
+ // Spiral search if random failed
if (!foundPosition) {
var baseStart = PathSystem.getPathStart(isPlayerSide);
- var searchRadius = minDistance; // Start searching just outside the minimum distance.
- var angleStep = Math.PI / 6; // Check 12 directions.
- var spiralAttempts = 30;
- for (var i = 0; i < spiralAttempts; i++) {
+ var searchRadius = minDistance;
+ var angleStep = Math.PI / 6;
+ spiral: for (var i = 0; i < 30; i++) {
for (var angle = 0; angle < Math.PI * 2; angle += angleStep) {
var testX = baseStart.x + Math.cos(angle) * searchRadius;
var testY = baseStart.y + Math.sin(angle) * searchRadius;
- var isCandidateValid = true;
- for (var j = 0; j < activeChips.length; j++) {
- var otherChip = activeChips[j];
+ var isValid = activeChips.every(function (otherChip) {
var dx = testX - otherChip.x;
var dy = testY - otherChip.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < minDistance) {
- isCandidateValid = false;
- break;
- }
- }
- if (isCandidateValid) {
+ return Math.sqrt(dx * dx + dy * dy) >= minDistance;
+ });
+ if (isValid) {
newPos = {
x: testX,
y: testY
};
foundPosition = true;
- break;
+ break spiral;
}
}
- if (foundPosition) {
- break;
- }
- searchRadius += 20; // Spiral outwards.
+ searchRadius += 20;
}
}
- // Ultimate fallback if no position is found (should be very rare).
- if (!foundPosition) {
- newPos = PathSystem.getPathStart(isPlayerSide);
- }
}
chip.activate(value, isPlayerSide, newPos);
- // IMPORTANT: Add chip to active array BEFORE the container
- // This ensures that subsequent chips spawned in the same batch
- // will detect this chip when checking for overlaps
if (isPlayerSide) {
activePlayerChips.push(chip);
activePlayerChipsContainer.addChild(chip);
} else {
activeAIChips.push(chip);
activeAIChipsContainer.addChild(chip);
}
- },
- spawnChipAtPosition: function spawnChipAtPosition(value, isPlayerSide, position) {
- var chip = PoolManager.getChip();
- if (!chip) {
- return;
- }
- chip.activate(value, isPlayerSide, position);
- // Add chip to active array and container
- if (isPlayerSide) {
- activePlayerChips.push(chip);
- activePlayerChipsContainer.addChild(chip);
- } else {
- activeAIChips.push(chip);
- activeAIChipsContainer.addChild(chip);
- }
}
};
/****
* Mod System
@@ -1547,9 +1338,8 @@
},
currentlyDisplayedMod: null,
modDisplayContainer: null,
topSuitGraphics: [],
- // Store references to top suit graphics
init: function init() {
if (storage.equippedMods) {
this.equippedMods = storage.equippedMods;
}
@@ -1558,67 +1348,61 @@
storage.equippedMods = this.equippedMods;
},
equipMod: function equipMod(modAssetId) {
var modData = this.modData[modAssetId];
- if (!modData) {
- return;
- }
+ if (!modData) return;
this.equippedMods[modData.suit] = modAssetId;
this.saveToStorage();
this.updateTopSuitDisplay();
this.hideModDisplay();
},
updateTopSuitDisplay: function updateTopSuitDisplay() {
+ var _this3 = this;
var suitAssets = ['heartSuit', 'diamondSuit', 'clubSuit', 'spadeSuit'];
var suits = ['hearts', 'diamonds', 'clubs', 'spades'];
- for (var i = 0; i < this.topSuitGraphics.length; i++) {
- var container = this.topSuitGraphics[i];
- if (container) {
- // Remove the previous mod/suit icon if it exists (it will be at index 1)
- if (container.children.length > 1) {
- container.removeChildAt(1);
- }
- // Add the appropriate icon (either mod or default suit)
- var suit = suits[i];
- var equippedMod = this.equippedMods[suit];
- var assetId = equippedMod || suitAssets[i];
- var suitIcon = LK.getAsset(assetId, {
- anchorX: 0.5,
- anchorY: 0.5
- });
- suitIcon.scale.set(1.2);
- // Center it in the background card
- suitIcon.x = 0;
- suitIcon.y = 0;
- container.addChild(suitIcon);
+ this.topSuitGraphics.forEach(function (container, i) {
+ if (!container) return;
+ // Remove previous icon
+ if (container.children.length > 1) {
+ container.removeChildAt(1);
}
- }
+ // Add new icon
+ var suit = suits[i];
+ var assetId = _this3.equippedMods[suit] || suitAssets[i];
+ var suitIcon = LK.getAsset(assetId, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ suitIcon.scale.set(1.2);
+ suitIcon.x = 0;
+ suitIcon.y = 0;
+ container.addChild(suitIcon);
+ });
},
showModDisplay: function showModDisplay(modAssetId, sourceX, sourceY) {
- if (this.currentlyDisplayedMod) {
- return;
- }
+ var _this4 = this;
+ if (this.currentlyDisplayedMod) return;
var modData = this.modData[modAssetId];
- if (!modData) {
- return;
- }
+ if (!modData) return;
this.currentlyDisplayedMod = modAssetId;
this.modDisplayContainer = new Container();
this.modDisplayContainer.interactive = true;
uiLayer.addChild(this.modDisplayContainer);
- // Add a semi-transparent background to catch clicks outside the popup
+ // Background blocker
var bgBlocker = new Container();
bgBlocker.interactive = true;
bgBlocker.hitArea = new Rectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
this.modDisplayContainer.addChild(bgBlocker);
+ // Mod display
var enlargedMod = LK.getAsset(modAssetId, {
anchorX: 0.5,
anchorY: 0.5
});
enlargedMod.x = SCREEN_WIDTH / 2;
enlargedMod.y = SCREEN_HEIGHT / 2 - 450;
enlargedMod.scale.set(1);
this.modDisplayContainer.addChild(enlargedMod);
+ // Description overlay
var overlayWidth = 600;
var overlayHeight = 400;
var descriptionOverlay = new Container();
var overlayBg = LK.getAsset('card', {
@@ -1675,11 +1459,10 @@
descriptionOverlay.x = SCREEN_WIDTH / 2;
descriptionOverlay.y = SCREEN_HEIGHT / 2 + 250;
descriptionOverlay.alpha = 0;
this.modDisplayContainer.addChild(descriptionOverlay);
- var self = this;
equipButton.down = function () {
- self.equipMod(modAssetId);
+ return _this4.equipMod(modAssetId);
};
tween(enlargedMod, {
scaleX: 4,
scaleY: 4
@@ -1693,208 +1476,206 @@
duration: 200,
delay: 150
});
bgBlocker.down = function () {
- self.hideModDisplay();
+ return _this4.hideModDisplay();
};
},
hideModDisplay: function hideModDisplay() {
- if (!this.modDisplayContainer) {
- return;
- }
- var self = this;
+ var _this5 = this;
+ if (!this.modDisplayContainer) return;
tween(this.modDisplayContainer, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
- if (self.modDisplayContainer && self.modDisplayContainer.parent) {
- self.modDisplayContainer.parent.removeChild(self.modDisplayContainer);
+ if (_this5.modDisplayContainer && _this5.modDisplayContainer.parent) {
+ _this5.modDisplayContainer.parent.removeChild(_this5.modDisplayContainer);
}
- self.modDisplayContainer = null;
- self.currentlyDisplayedMod = null;
+ _this5.modDisplayContainer = null;
+ _this5.currentlyDisplayedMod = null;
}
});
},
getEquippedModAsset: function getEquippedModAsset(suit) {
return this.equippedMods[suit] || null;
}
};
/****
-* Wave Spawning System
+* Wave System
****/
var WaveSystem = {
playerSpawnTimer: 0,
aiSpawnTimer: 0,
waveNumber: 1,
waveTimer: 0,
waveDuration: 1800,
- // 30 seconds per wave (30 * 60 ticks)
spawnInterval: 45,
- // Much faster: spawn every 0.75 seconds instead of 2 seconds
bossSpawned: false,
- // Track if boss has been spawned this wave
+ playerBossDefeated: false,
+ aiBossDefeated: false,
getChipValue: function getChipValue(waveNumber) {
- // Wave 1: Only 1-chips (easier)
- if (waveNumber === 1) {
- return 1;
- }
- // Wave 2-3: More 5s introduced earlier for increased difficulty
- else if (waveNumber <= 3) {
- return Math.random() < 0.2 ? 5 : 1; // 20% chance of 5-chip (increased from 10%)
- }
- // Wave 4-6: Even more 5s mixed in
- else if (waveNumber <= 6) {
- return Math.random() < 0.35 ? 5 : 1; // 35% chance of 5-chip (increased from 25%)
- }
- // Wave 7-9: More 5s than 1s for increased difficulty
- else if (waveNumber <= 9) {
- return Math.random() < 0.65 ? 5 : 1; // 65% chance of 5-chip (increased from 50%)
- }
- // Wave 11-15: Introduce 10-chips earlier and more frequently
- else if (waveNumber <= 15) {
- var rand = Math.random();
- if (rand < 0.15) {
- return 10;
- } else if (rand < 0.7) {
- return 5;
- } else {
- return 1;
+ var waveConfigs = [{
+ maxWave: 1,
+ values: [{
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: 3,
+ values: [{
+ chance: 0.2,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: 6,
+ values: [{
+ chance: 0.35,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: 9,
+ values: [{
+ chance: 0.65,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: 15,
+ values: [{
+ chance: 0.15,
+ value: 10
+ }, {
+ chance: 0.7,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: 19,
+ values: [{
+ chance: 0.3,
+ value: 10
+ }, {
+ chance: 0.8,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }, {
+ maxWave: Infinity,
+ values: [{
+ chance: 0.08,
+ value: 25
+ }, {
+ chance: 0.4,
+ value: 10
+ }, {
+ chance: 0.85,
+ value: 5
+ }, {
+ chance: 1,
+ value: 1
+ }]
+ }];
+ var config = waveConfigs.find(function (c) {
+ return waveNumber <= c.maxWave;
+ });
+ var rand = Math.random();
+ var _iterator = _createForOfIteratorHelper(config.values),
+ _step;
+ try {
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
+ var _step$value = _step.value,
+ chance = _step$value.chance,
+ value = _step$value.value;
+ if (rand < chance) return value;
}
+ } catch (err) {
+ _iterator.e(err);
+ } finally {
+ _iterator.f();
}
- // Wave 16-19: More variety with higher values
- else if (waveNumber <= 19) {
- var rand = Math.random();
- if (rand < 0.3) {
- return 10;
- } else if (rand < 0.8) {
- return 5;
- } else {
- return 1;
- }
- }
- // Wave 21+: Keep scaling gradually with higher difficulty
- else if (!this.isBossWave(waveNumber)) {
- var rand = Math.random();
- if (rand < 0.08) {
- return 25;
- } else if (rand < 0.4) {
- return 10;
- } else if (rand < 0.85) {
- return 5;
- } else {
- return 1;
- }
- }
+ return 1;
},
getBossChipValue: function getBossChipValue(waveNumber) {
- // The health of the first boss is based on the total health of the preceding wave.
- // Subsequent bosses have their health increased proportionally (doubling each time).
var bossLevel = Math.floor(waveNumber / 10);
- // Base value derived from total health of wave 9 enemies (~10.5k health -> 210 value)
var baseBossValue = 210;
return baseBossValue * Math.pow(2, bossLevel - 1);
},
isBossWave: function isBossWave(waveNumber) {
return waveNumber % 10 === 0;
},
spawnChip: function spawnChip(isPlayerSide) {
if (this.isBossWave(this.waveNumber)) {
- // Boss wave: spawn ONE big enemy at the start, then nothing
if (!this.bossSpawned) {
var bossValue = this.getBossChipValue(this.waveNumber);
ChipSpawner.spawnChip(bossValue, isPlayerSide);
this.bossSpawned = true;
- console.log("BOSS spawned with value:", bossValue);
}
- // Don't spawn anything else during boss wave
return;
}
- // Normal wave spawning - always single enemy
var chipValue = this.getChipValue(this.waveNumber);
ChipSpawner.spawnChip(chipValue, isPlayerSide);
},
- playerBossDefeated: false,
- aiBossDefeated: false,
update: function update() {
this.waveTimer++;
- // Check if wave is complete
if (this.waveTimer >= this.waveDuration) {
- // If it's a boss wave, only advance if both bosses are defeated.
if (this.isBossWave(this.waveNumber) && (!this.playerBossDefeated || !this.aiBossDefeated)) {
- // Don't end the wave, just let it continue until bosses are defeated.
+ // Wait for boss defeat
} else {
- // For normal waves, or for boss waves where both bosses are defeated.
- // Check if both bosses were defeated in a boss wave
if (this.isBossWave(this.waveNumber) && this.playerBossDefeated && this.aiBossDefeated) {
- console.log("Both bosses defeated! Moving to next round!");
createFloatingText('ROUND COMPLETE!', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0x00ff00);
- // Reset boss defeat flags
this.playerBossDefeated = false;
this.aiBossDefeated = false;
}
this.waveTimer = 0;
this.waveNumber++;
this.playerSpawnTimer = 0;
this.aiSpawnTimer = 0;
- this.bossSpawned = false; // Reset boss spawn flag
- var waveType = this.isBossWave(this.waveNumber) ? "BOSS WAVE" : "Wave";
- console.log(waveType + " " + this.waveNumber + " starting!");
+ this.bossSpawned = false;
return;
}
}
- // BOSS WAVE LOGIC - Spawn bosses immediately at start of wave
if (this.isBossWave(this.waveNumber)) {
if (!this.bossSpawned && this.waveTimer === 1) {
- // Only on first tick of boss wave
- console.log("=== BOSS WAVE " + this.waveNumber + " STARTING ===");
- // Spawn player boss
- var playerBossValue = this.getBossChipValue(this.waveNumber);
- ChipSpawner.spawnChip(playerBossValue, true);
- console.log("PLAYER BOSS spawned with value:", playerBossValue, "Active player chips:", activePlayerChips.length);
- // Spawn AI boss
- var aiBossValue = this.getBossChipValue(this.waveNumber);
- ChipSpawner.spawnChip(aiBossValue, false);
- console.log("AI BOSS spawned with value:", aiBossValue, "Active AI chips:", activeAIChips.length);
+ var bossValue = this.getBossChipValue(this.waveNumber);
+ ChipSpawner.spawnChip(bossValue, true);
+ ChipSpawner.spawnChip(bossValue, false);
this.bossSpawned = true;
}
- // Boss defeat detection - only after bosses have had time to spawn
if (this.bossSpawned && this.waveTimer > 120) {
- // 2 second delay
- console.log("Checking boss status - Player chips:", activePlayerChips.length, "AI chips:", activeAIChips.length);
- // Check if player side boss is defeated
if (!this.playerBossDefeated && activePlayerChips.length === 0) {
this.playerBossDefeated = true;
- console.log("Player defeated the boss!");
createFloatingText('BOSS DEFEATED!', SCREEN_WIDTH / 2, PLAYER_AREA_Y + SLOT_HEIGHT, 0xffd700);
}
- // Check if AI side boss is defeated
if (!this.aiBossDefeated && activeAIChips.length === 0) {
this.aiBossDefeated = true;
- console.log("AI defeated the boss!");
createFloatingText('AI BOSS DEFEATED!', SCREEN_WIDTH / 2, AI_AREA_Y + SLOT_HEIGHT, 0xffd700);
}
- // If both bosses defeated, immediately end the wave
if (this.playerBossDefeated && this.aiBossDefeated) {
- this.waveTimer = this.waveDuration - 1; // Set to end on next tick
+ this.waveTimer = this.waveDuration - 1;
}
}
- return; // Don't do normal spawning during boss waves
+ return;
}
- // NORMAL WAVE LOGIC
- var currentSpawnInterval = this.spawnInterval;
- if (this.waveNumber === 1) {
- currentSpawnInterval = 90; // Slower spawning for wave 1 (1.5 seconds)
- } else if (this.waveNumber >= 2) {
- currentSpawnInterval = Math.max(45, this.spawnInterval - Math.floor((this.waveNumber - 2) * 2)); // Faster spawning for wave 2+
- }
- // Spawn on player side
+ // Normal wave spawning
+ var currentSpawnInterval = this.waveNumber === 1 ? 90 : Math.max(45, this.spawnInterval - Math.floor((this.waveNumber - 2) * 2));
this.playerSpawnTimer++;
if (this.playerSpawnTimer >= currentSpawnInterval) {
this.playerSpawnTimer = 0;
this.spawnChip(true);
}
- // Spawn on AI side
this.aiSpawnTimer++;
if (this.aiSpawnTimer >= currentSpawnInterval) {
this.aiSpawnTimer = 0;
this.spawnChip(false);
@@ -1905,45 +1686,45 @@
* Game State
****/
var gameState = {
playerChips: 200,
- // Increased starting gold
aiChips: 200,
playerLives: 3,
aiLives: 3,
isPlayerTurn: true,
dealCost: 25,
- // Initial deal cost
dealCount: 0,
playerDeck: [],
playerHand: [],
playerPlayArea: [],
aiDeck: [],
aiPlayArea: []
};
-// Game state management
-var currentGameState = 'start'; // 'start' or 'playing'
-var startScreenElements = [];
-var gameElements = [];
/****
* Game Variables
****/
+var currentGameState = 'start';
+var startScreenElements = [];
+var gameElements = [];
var activePlayerChips = [];
var activeAIChips = [];
var activeBullets = [];
var playerHandNameTexts = [];
var backgroundSuits = [];
var selectedCard = null;
var isDragging = false;
var originalCardPosition = null;
-var activePlayerChipsContainer = new Container(); // Container for player chips
-var activeAIChipsContainer = new Container(); // Container for AI chips
+var activePlayerChipsContainer = new Container();
+var activeAIChipsContainer = new Container();
var playerLifeHearts = [];
var aiLifeHearts = [];
var opponentNameText = null;
var playerNameText = null;
var lastPlayerLives = 0;
var lastAiLives = 0;
+/****
+* Initialize Layers
+****/
var gameLayer = new Container();
var floorBackground = LK.getAsset('floorbackround', {
anchorX: 0.5,
anchorY: 0.5
@@ -1968,9 +1749,8 @@
playerChipsText.y = SCREEN_HEIGHT - 120;
playerChipsText.visible = false;
uiLayer.addChild(playerChipsText);
gameElements.push(playerChipsText);
-// Add AI stats too for clarity
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xffffff,
weight: 800,
@@ -1981,8 +1761,9 @@
waveText.y = 50;
waveText.visible = false;
uiLayer.addChild(waveText);
gameElements.push(waveText);
+// Deal/Discard button
var discardAreaContainer = new Container();
var discardAreaGraphic = discardAreaContainer.attachAsset('dealButton', {
anchorX: 0.5,
anchorY: 0.5
@@ -1996,196 +1777,55 @@
});
discardText.anchor.set(0.5, 0.5);
discardText.y = 110;
discardAreaContainer.addChild(discardText);
-// Position it right of the hand
-var handWidthForDiscard = 5 * DEAL_SLOT_WIDTH + 4 * 30;
-var handStartXForDiscard = (SCREEN_WIDTH - handWidthForDiscard) / 2;
-var discardX = handStartXForDiscard + handWidthForDiscard + 30 + DEAL_SLOT_WIDTH / 2;
-// Make sure it doesn't go off screen
-if (discardX + DEAL_SLOT_WIDTH / 2 > SCREEN_WIDTH) {
- discardX = SCREEN_WIDTH - DEAL_SLOT_WIDTH / 2 - 20;
-}
-discardAreaContainer.x = discardX + 10 + 10;
-discardAreaContainer.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2 + 10 - 10;
+var handStartXForDiscard = (SCREEN_WIDTH - HAND_WIDTH) / 2;
+var discardX = Math.min(handStartXForDiscard + HAND_WIDTH + 30 + DEAL_SLOT_WIDTH / 2 + 20, SCREEN_WIDTH - DEAL_SLOT_WIDTH / 2 - 20);
+discardAreaContainer.x = discardX;
+discardAreaContainer.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
discardAreaContainer.visible = false;
uiLayer.addChild(discardAreaContainer);
gameElements.push(discardAreaContainer);
discardAreaContainer.down = function () {
if (!isDragging && gameState.playerChips >= gameState.dealCost) {
dealNewHand();
}
};
-// Add this new cleanup function:
-function cleanupDeadChips() {
- // Clean player chips
- for (var i = activePlayerChips.length - 1; i >= 0; i--) {
- var chip = activePlayerChips[i];
- if (!chip.active || chip.health <= 0) {
- console.log("Force-cleaning dead player chip");
- PoolManager.returnChip(chip);
- }
- }
- // Clean AI chips
- for (var i = activeAIChips.length - 1; i >= 0; i--) {
- var chip = activeAIChips[i];
- if (!chip.active || chip.health <= 0) {
- console.log("Force-cleaning dead AI chip");
- PoolManager.returnChip(chip);
- }
- }
-}
/****
* Game Functions
****/
-function createBurnEffect(x, y) {
- var numParticles = 4; // Slightly more particles
- for (var i = 0; i < numParticles; i++) {
- var particle = LK.getAsset('burnHeartMod', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- particle.x = x + (Math.random() - 0.5) * 80; // Wider spread
- particle.y = y + (Math.random() - 0.5) * 80;
- particle.scale.set(0.3 + Math.random() * 0.2); // Adjusted for new asset
- var angle = Math.random() * Math.PI * 2;
- var distance = Math.random() * 40 + 25; // Larger movement distance
- var duration = 400 + Math.random() * 500; // Slightly longer duration
- var targetX = x + Math.cos(angle) * distance;
- var targetY = y + Math.sin(angle) * distance - Math.random() * 50; // Float upward more
- gameLayer.addChild(particle);
- tween(particle, {
- x: targetX,
- y: targetY,
- alpha: 0,
- scaleX: 0.1,
- scaleY: 0.1
- }, {
- duration: duration,
- easing: tween.quadOut,
- onFinish: function (p) {
- return function () {
- if (p.parent) {
- p.parent.removeChild(p);
- }
- };
- }(particle)
- });
- }
-}
-function createIceSparkles(x, y) {
- var numSparkles = 2;
- for (var i = 0; i < numSparkles; i++) {
- var sparkle = LK.getAsset('iceCube', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- sparkle.x = x + (Math.random() - 0.5) * 60;
- sparkle.y = y + (Math.random() - 0.5) * 60;
- sparkle.scale.set(0.1 + Math.random() * 0.1);
- sparkle.alpha = 0.6;
- sparkle.tint = 0x88ddff; // Light blue tint
- gameLayer.addChild(sparkle);
- tween(sparkle, {
- y: sparkle.y - 20,
- alpha: 0,
- scaleX: 0.05,
- scaleY: 0.05
- }, {
- duration: 800,
- easing: tween.quadOut,
- onFinish: function (p) {
- return function () {
- if (p.parent) {
- p.parent.removeChild(p);
- }
- };
- }(sparkle)
- });
- }
-}
-function createIceShatterEffect(x, y) {
- var numShards = 6;
- for (var i = 0; i < numShards; i++) {
- var shard = LK.getAsset('iceCube', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- shard.x = x;
- shard.y = y;
- shard.scale.set(0.2 + Math.random() * 0.2);
- shard.tint = 0xaaeeff; // Ice blue tint
- var angle = Math.PI * 2 * i / numShards + (Math.random() - 0.5) * 0.5;
- var distance = 40 + Math.random() * 30;
- var targetX = x + Math.cos(angle) * distance;
- var targetY = y + Math.sin(angle) * distance;
- gameLayer.addChild(shard);
- tween(shard, {
- x: targetX,
- y: targetY,
- alpha: 0,
- rotation: Math.random() * Math.PI * 2,
- scaleX: 0.05,
- scaleY: 0.05
- }, {
- duration: 600,
- easing: tween.quadOut,
- onFinish: function (p) {
- return function () {
- if (p.parent) {
- p.parent.removeChild(p);
- }
- };
- }(shard)
- });
- }
-}
-// Add this new function to calculate greed bonus
function calculateGreedBonus(isPlayerSide, baseChipsEarned) {
- // Only calculate bonus if greed mod is equipped
- if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') {
- return 0;
- }
+ if (ModSystem.getEquippedModAsset('diamonds') !== 'chipsDiamondMod') return 0;
var playArea = isPlayerSide ? gameState.playerPlayArea : gameState.aiPlayArea;
var diamondCount = 0;
- // Count diamond cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = playArea[row][col];
- if (card && card.cardData.suit === 'diamonds') {
- diamondCount++;
- }
+ if (card && card.cardData.suit === 'diamonds') diamondCount++;
}
}
- // 5% bonus per diamond card (caps at 50% for full diamond board)
var bonusPercentage = diamondCount * 0.05;
return Math.ceil(baseChipsEarned * bonusPercentage);
}
function createStartScreen() {
ModSystem.init();
- // Clear any existing start screen elements
startScreenElements.forEach(function (element) {
- if (element.parent) {
- element.parent.removeChild(element);
- }
+ if (element.parent) element.parent.removeChild(element);
});
startScreenElements = [];
- // Add background animation
+ // Background animation
var startScreenAnimContainer = new Container();
uiLayer.addChild(startScreenAnimContainer);
startScreenElements.push(startScreenAnimContainer);
var suitAssets = ['heartSuit', 'diamondSuit', 'clubSuit', 'spadeSuit'];
var spacing = 400;
var numCols = Math.ceil(SCREEN_WIDTH / spacing) + 3;
var numRows = Math.ceil(SCREEN_HEIGHT / spacing) + 3;
- var patternWidth = numCols * spacing;
- var patternHeight = numRows * spacing;
backgroundSuits = [];
for (var row = 0; row < numRows; row++) {
for (var col = 0; col < numCols; col++) {
var suitIndex = (row + col) % suitAssets.length;
- var suitId = suitAssets[suitIndex];
- var suit = LK.getAsset(suitId, {
+ var suit = LK.getAsset(suitAssets[suitIndex], {
anchorX: 0.5,
anchorY: 0.5
});
suit.x = col * spacing - spacing;
@@ -2197,90 +1837,87 @@
startScreenAnimContainer.addChild(suit);
backgroundSuits.push(suit);
}
}
+ // Logo
var gameLogo = LK.getAsset('titleLogo', {
anchorX: 0.5,
anchorY: 0.5
});
gameLogo.x = SCREEN_WIDTH / 2;
gameLogo.y = SCREEN_HEIGHT / 2 - 200;
uiLayer.addChild(gameLogo);
startScreenElements.push(gameLogo);
+ // Bottom bar
var startBottomBar = LK.getAsset('bottomBar', {
anchorX: 0.5,
anchorY: 1
});
startBottomBar.x = SCREEN_WIDTH / 2;
startBottomBar.y = SCREEN_HEIGHT;
uiLayer.addChild(startBottomBar);
startScreenElements.push(startBottomBar);
- // --- Title screen selection highlight asset ---
+ // Selection highlight
var titleScreenSelection = LK.getAsset('titleScreenSelection', {
anchorX: 0.5,
anchorY: 1
});
titleScreenSelection.alpha = 0.3;
uiLayer.addChild(titleScreenSelection);
startScreenElements.push(titleScreenSelection);
- // --- Track current selection: "battle" or "mods" ---
var currentTitleScreenSelection = "battle";
- // --- Calculate button positions for highlight ---
+ // Play button and icon
var playButton = LK.getAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
interactive: true
});
playButton.x = SCREEN_WIDTH / 2;
playButton.y = SCREEN_HEIGHT - 100;
- // Add battleIcon directly above playButton
var battleIcon = LK.getAsset('battleIcon', {
anchorX: 0.5,
anchorY: 1,
interactive: true
});
battleIcon.x = playButton.x;
- battleIcon.y = playButton.y - playButton.height / 2 - 10; // 30px gap above playButton
+ battleIcon.y = playButton.y - playButton.height / 2 - 10;
uiLayer.addChild(battleIcon);
- startScreenElements.push(battleIcon);
uiLayer.addChild(playButton);
+ startScreenElements.push(battleIcon);
startScreenElements.push(playButton);
- // --- Suit mod button and mods icon ---
+ // Mods button and icon
var suitModButton = LK.getAsset('suitModButton', {
anchorX: 0.5,
anchorY: 0.5,
interactive: true
});
suitModButton.x = playButton.x + playButton.width / 2 + 50 + suitModButton.width / 2;
suitModButton.y = playButton.y;
- // Add modsIcon directly above suitModButton, same orientation as battleIcon
var modsIcon = LK.getAsset('modsIcon', {
anchorX: 0.5,
anchorY: 1,
interactive: true
});
modsIcon.x = suitModButton.x;
- modsIcon.y = suitModButton.y - suitModButton.height / 2 - 10; // 10px gap above suitModButton
+ modsIcon.y = suitModButton.y - suitModButton.height / 2 - 10;
uiLayer.addChild(modsIcon);
- startScreenElements.push(modsIcon);
uiLayer.addChild(suitModButton);
+ startScreenElements.push(modsIcon);
startScreenElements.push(suitModButton);
- // --- Selection highlight logic ---
- // Helper to move the highlight to the correct button
+ // Selection highlight functions
function updateTitleScreenSelectionHighlight() {
var targetX, targetY, targetW, targetH;
if (currentTitleScreenSelection === "battle") {
targetX = playButton.x;
- targetY = playButton.y + playButton.height / 2; // anchorY:1, so bottom edge
+ targetY = playButton.y + playButton.height / 2;
targetW = playButton.width * 1.1;
- targetH = playButton.height + battleIcon.height + 20; // Cover button, icon, and gap with padding
- } else if (currentTitleScreenSelection === "mods") {
+ targetH = playButton.height + battleIcon.height + 20;
+ } else {
targetX = suitModButton.x;
targetY = suitModButton.y + suitModButton.height / 2;
targetW = suitModButton.width * 1.1;
- targetH = suitModButton.height + modsIcon.height + 20; // Cover button, icon, and gap with padding
+ targetH = suitModButton.height + modsIcon.height + 20;
}
- // Animate movement and scaling for smoothness
tween.stop(titleScreenSelection);
tween(titleScreenSelection, {
x: targetX,
y: targetY,
@@ -2290,58 +1927,50 @@
duration: 180,
easing: tween.cubicOut
});
}
- // Set initial size and position for highlight (battle by default)
+ // Set initial highlight
titleScreenSelection.width = playButton.width * 1.2;
titleScreenSelection.height = playButton.height + battleIcon.height + 20;
titleScreenSelection.x = playButton.x;
titleScreenSelection.y = playButton.y + playButton.height / 2;
- // --- Selection switching logic ---
- // Only two screens: "battle" and "mods"
- playButton.down = function () {
+ // Button handlers
+ playButton.down = battleIcon.down = function () {
if (currentTitleScreenSelection !== "battle") {
currentTitleScreenSelection = "battle";
updateTitleScreenSelectionHighlight();
}
startGame();
};
- suitModButton.down = function () {
+ suitModButton.down = modsIcon.down = function () {
if (currentTitleScreenSelection !== "mods") {
currentTitleScreenSelection = "mods";
updateTitleScreenSelectionHighlight();
}
- var isModsVisible = modsContainer.visible;
- modsContainer.visible = !isModsVisible;
- gameLogo.visible = isModsVisible;
- if (modsContainer.visible) {
- ModSystem.updateTopSuitDisplay();
- }
+ modsContainer.visible = !modsContainer.visible;
+ gameLogo.visible = !modsContainer.visible;
+ if (modsContainer.visible) ModSystem.updateTopSuitDisplay();
};
- battleIcon.down = playButton.down;
- modsIcon.down = suitModButton.down;
- // --- Mods container as before ---
+ // Mods container
var modsContainer = new Container();
modsContainer.visible = false;
uiLayer.addChild(modsContainer);
startScreenElements.push(modsContainer);
- var numCircles = suitAssets.length;
var totalBarWidth = SCREEN_WIDTH - 200;
- var suitSpacing = totalBarWidth / numCircles;
+ var suitSpacing = totalBarWidth / suitAssets.length;
var startX = SCREEN_WIDTH / 2 - totalBarWidth / 2 + suitSpacing / 2;
var yOffset = SCREEN_HEIGHT * 0.1;
var circleY = 300 + yOffset;
- ModSystem.topSuitGraphics = []; // Reset the array
- for (var i = 0; i < numCircles; i++) {
- var circleX = startX + i * suitSpacing;
+ ModSystem.topSuitGraphics = [];
+ for (var i = 0; i < suitAssets.length; i++) {
var suitContainer = new Container();
var circleBg = LK.getAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
circleBg.scale.set(1.5);
suitContainer.addChild(circleBg);
- suitContainer.x = circleX;
+ suitContainer.x = startX + i * suitSpacing;
suitContainer.y = circleY - SCREEN_HEIGHT * 0.05;
modsContainer.addChild(suitContainer);
ModSystem.topSuitGraphics.push(suitContainer);
}
@@ -2351,68 +1980,59 @@
});
lineBreak.x = SCREEN_WIDTH / 2;
lineBreak.y = circleY + 360 - SCREEN_HEIGHT * 0.05;
modsContainer.addChild(lineBreak);
+ // Mod grid
var gridContainer = new Container();
gridContainer.x = SCREEN_WIDTH / 2;
gridContainer.y = circleY + 450 - SCREEN_HEIGHT * 0.05;
modsContainer.addChild(gridContainer);
- var cols = 4;
- var rows = 3;
var cardForSizing = LK.getAsset('card', {});
var cellWidth = cardForSizing.width;
var cellHeight = cardForSizing.height;
var colSpacing = 160;
var rowSpacing = 140;
- var gridTotalWidth = cols * cellWidth + (cols - 1) * colSpacing;
+ var gridTotalWidth = 4 * cellWidth + 3 * colSpacing;
var gridStartX = -gridTotalWidth / 2;
var gridStartY = 50;
var suitModAssets = ['burnHeartMod', 'chipsDiamondMod', 'spreadClubMod', 'freezeSpadeMod'];
- for (var r = 0; r < rows; r++) {
- for (var c = 0; c < cols; c++) {
+ for (var r = 0; r < 3; r++) {
+ for (var c = 0; c < 4; c++) {
var cell = LK.getAsset('card', {
anchorX: 0,
anchorY: 0
});
cell.scale.set(1.3);
cell.x = gridStartX + c * (cellWidth + colSpacing);
cell.y = gridStartY + r * (cellHeight + rowSpacing);
gridContainer.addChild(cell);
- if (r === 0) {
- var modAssetId = suitModAssets[c];
- if (modAssetId) {
- var modAsset = LK.getAsset(modAssetId, {
- anchorX: 0.5,
- anchorY: 0.5
- });
- modAsset.x = cell.x + cell.width * cell.scale.x / 2;
- modAsset.y = cell.y + cell.height * cell.scale.y / 2;
- gridContainer.addChild(modAsset);
- // Add click handler for mod display to the entire cell card
- cell.interactive = true;
- (function (assetId, modAssetRef) {
- cell.down = function () {
- ModSystem.showModDisplay(assetId, modAssetRef.x, modAssetRef.y);
- };
- })(modAssetId, modAsset);
- }
+ if (r === 0 && suitModAssets[c]) {
+ var modAsset = LK.getAsset(suitModAssets[c], {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ modAsset.x = cell.x + cell.width * cell.scale.x / 2;
+ modAsset.y = cell.y + cell.height * cell.scale.y / 2;
+ gridContainer.addChild(modAsset);
+ cell.interactive = true;
+ cell.down = function (assetId, modRef) {
+ return function () {
+ return ModSystem.showModDisplay(assetId, modRef.x, modRef.y);
+ };
+ }(suitModAssets[c], modAsset);
}
}
}
currentGameState = 'start';
}
function startGame() {
- // Clear start screen elements
startScreenElements.forEach(function (element) {
- if (element.parent) {
- element.parent.removeChild(element);
- }
+ if (element.parent) element.parent.removeChild(element);
});
startScreenElements = [];
backgroundSuits = [];
- // Show game elements
gameElements.forEach(function (element) {
- element.visible = true;
+ return element.visible = true;
});
currentGameState = 'playing';
initializeGame();
}
@@ -2420,64 +2040,45 @@
ModSystem.init();
PoolManager.init();
PathSystem.init();
// Initialize play areas
- gameState.playerPlayArea = [];
- gameState.aiPlayArea = [];
+ gameState.playerPlayArea = Array(PLAY_AREA_ROWS).fill().map(function () {
+ return Array(PLAY_AREA_COLS).fill(null);
+ });
+ gameState.aiPlayArea = Array(PLAY_AREA_ROWS).fill().map(function () {
+ return Array(PLAY_AREA_COLS).fill(null);
+ });
playerHandNameTexts = [null, null];
- for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- gameState.playerPlayArea[row] = [];
- gameState.aiPlayArea[row] = [];
- for (var col = 0; col < PLAY_AREA_COLS; col++) {
- gameState.playerPlayArea[row][col] = null;
- gameState.aiPlayArea[row][col] = null;
- }
- }
- // Create initial decks
+ // Create decks
gameState.playerDeck = CardSystem.createDeck();
gameState.aiDeck = CardSystem.createDeck();
- // Draw grid lines
+ // Setup UI
drawPlayAreas();
- drawPaths();
createLifeDisplays();
lastPlayerLives = gameState.playerLives;
lastAiLives = gameState.aiLives;
- // Initialize player's hand with empty slots
- gameState.playerHand = [null, null, null, null, null];
- // Center bar removed
- // Start the wave system - first waves begin immediately
+ // Initialize player hand
+ gameState.playerHand = Array(5).fill(null);
+ // Start spawning
LK.setTimeout(function () {
- ChipSpawner.spawnChip(1, true); // First enemy on player side
- ChipSpawner.spawnChip(1, false); // First enemy on AI side
+ ChipSpawner.spawnChip(1, true);
+ ChipSpawner.spawnChip(1, false);
}, 1000);
}
function createLifeDisplays() {
- // Clear any existing hearts and labels
- if (opponentNameText && opponentNameText.parent) {
- opponentNameText.parent.removeChild(opponentNameText);
- }
- if (playerNameText && playerNameText.parent) {
- playerNameText.parent.removeChild(playerNameText);
- }
- playerLifeHearts.forEach(function (h) {
- if (h.parent) {
- h.parent.removeChild(h);
- }
+ // Clean up existing
+ [opponentNameText, playerNameText].concat(_toConsumableArray(playerLifeHearts), _toConsumableArray(aiLifeHearts)).forEach(function (element) {
+ if (element && element.parent) element.parent.removeChild(element);
});
playerLifeHearts = [];
- aiLifeHearts.forEach(function (h) {
- if (h.parent) {
- h.parent.removeChild(h);
- }
- });
aiLifeHearts = [];
var heartSpacing = 110;
var heartScale = 0.5;
- var startX_AI = 200 - 70;
- var startX_Player = SCREEN_WIDTH - 200 + 70;
- var yPos = SCREEN_HEIGHT / 2 - 137 + 10;
+ var startX_AI = 130;
+ var startX_Player = SCREEN_WIDTH - 130;
+ var yPos = SCREEN_HEIGHT / 2 - 127;
var labelYPos = yPos - 60;
- // Opponent Name Text
+ // Create name labels
var totalAIHeartsWidth = (gameState.aiLives - 1) * heartSpacing;
opponentNameText = new Text2('Opponent', {
size: 40,
fill: 0xffffff,
@@ -2486,9 +2087,8 @@
opponentNameText.anchor.set(0.5, 1);
opponentNameText.x = startX_AI + totalAIHeartsWidth / 2;
opponentNameText.y = labelYPos;
uiLayer.addChild(opponentNameText);
- // Player Name Text
var totalPlayerHeartsWidth = (gameState.playerLives - 1) * heartSpacing;
playerNameText = new Text2('Player', {
size: 40,
fill: 0xffffff,
@@ -2497,9 +2097,9 @@
playerNameText.anchor.set(0.5, 1);
playerNameText.x = startX_Player - totalPlayerHeartsWidth / 2;
playerNameText.y = labelYPos;
uiLayer.addChild(playerNameText);
- // AI Lives (left side)
+ // Create hearts
for (var i = 0; i < gameState.aiLives; i++) {
var heart = LK.getAsset('heartSuit', {
anchorX: 0.5,
anchorY: 0.5,
@@ -2510,9 +2110,8 @@
heart.y = yPos;
uiLayer.addChild(heart);
aiLifeHearts.push(heart);
}
- // Player Lives (right side)
for (var i = 0; i < gameState.playerLives; i++) {
var heart = LK.getAsset('heartSuit', {
anchorX: 0.5,
anchorY: 0.5,
@@ -2525,88 +2124,63 @@
playerLifeHearts.push(heart);
}
}
function dealNewHand() {
- if (gameState.playerChips < gameState.dealCost) {
- return;
- }
- // Find an empty slot in the player's hand
- var emptySlotIndex = -1;
- for (var i = 0; i < gameState.playerHand.length; i++) {
- if (!gameState.playerHand[i]) {
- emptySlotIndex = i;
- break;
- }
- }
- // If hand is full, do nothing
- if (emptySlotIndex === -1) {
- return;
- }
+ if (gameState.playerChips < gameState.dealCost) return;
+ var emptySlotIndex = gameState.playerHand.findIndex(function (card) {
+ return !card;
+ });
+ if (emptySlotIndex === -1) return;
gameState.playerChips -= gameState.dealCost;
gameState.dealCount++;
- gameState.dealCost = Math.floor(25 * Math.pow(1.05, gameState.dealCount)); // Lower base cost for single card
+ gameState.dealCost = Math.floor(25 * Math.pow(1.05, gameState.dealCount));
if (gameState.playerDeck.length === 0) {
gameState.playerDeck = CardSystem.createDeck();
}
var cardData = gameState.playerDeck.pop();
var card = new Card(cardData);
- // After every 10 waves, new cards dealt start one level higher
var startLevel = Math.floor((WaveSystem.waveNumber - 1) / 10) + 1;
card.setLevel(startLevel);
- var i = emptySlotIndex;
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
- var slotX = handStartX + i * DEAL_SLOT_WIDTH + i * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- // Start position - off to the bottom
- var startX = slotX + (Math.random() - 0.5) * 50; // Slight horizontal variance
+ var slotPos = getHandSlotPosition(emptySlotIndex);
+ var startX = slotPos.x + (Math.random() - 0.5) * 50;
var startY = SCREEN_HEIGHT + DEAL_SLOT_HEIGHT;
card.activate(startX, startY, false, true);
- card.rotation = 0; // No spin
- card.scaleX = 1; // Normal width
- card.scaleY = 0.01; // Extremely thin/squished vertically - almost completely flat
+ card.rotation = 0;
+ card.scaleX = 1;
+ card.scaleY = 0.01;
uiLayer.addChild(card);
- gameState.playerHand[i] = card;
- // Animate flying and expanding with more extreme squish and slower resolution
+ gameState.playerHand[emptySlotIndex] = card;
tween(card, {
- x: slotX,
- y: slotY,
- scaleY: 1.2 // More extreme overshoot on Y-axis
- }, {
- duration: 250,
- easing: tween.cubicOut,
- delay: i * 80,
+ x: slotPos.x,
+ y: slotPos.y,
+ scaleY: 1.2
+ }, Object.assign({}, ANIMATION_CONFIGS.cardDeal, {
+ delay: emptySlotIndex * 80,
onFinish: function onFinish() {
- // Settle back to normal size with longer duration
- tween(card, {
+ return tween(card, {
scaleY: 1
- }, {
- duration: 150,
- easing: tween.quadIn
- });
+ }, ANIMATION_CONFIGS.cardSettle);
}
- });
+ }));
updateUI();
}
function updateUI() {
playerChipsText.setText('Chips: ' + formatNumberWithSuffix(gameState.playerChips));
- // Update combined deal/discard button text and appearance when not dragging
if (!isDragging) {
discardText.setText('-' + formatNumberWithSuffix(gameState.dealCost));
discardText.fill = 0xffffff;
- // Update button color based on affordability
if (gameState.playerChips >= gameState.dealCost) {
- discardAreaGraphic.tint = 0xffffff; // No tint when affordable
- discardAreaGraphic.alpha = 1.0; // Full alpha when affordable
+ discardAreaGraphic.tint = 0xffffff;
+ discardAreaGraphic.alpha = 1.0;
} else {
- discardAreaGraphic.tint = 0x666666; // Grey tint when not affordable
- discardAreaGraphic.alpha = 1.0; // Keep full alpha even when not affordable
+ discardAreaGraphic.tint = 0x666666;
+ discardAreaGraphic.alpha = 1.0;
}
}
waveText.setText('Wave: ' + WaveSystem.waveNumber);
}
function drawPlayAreas() {
- // Draw player play area slots
+ // Player play area
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var slot = new Container();
var slotGraphics = slot.attachAsset('card', {
@@ -2618,42 +2192,37 @@
slot.y = PLAYER_AREA_Y + row * SLOT_HEIGHT + SLOT_HEIGHT / 2;
gameLayer.addChild(slot);
}
}
- // Draw AI play area slots
+ // AI play area
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var slot = new Container();
var slotGraphics = slot.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
slotGraphics.alpha = 0.5;
- slotGraphics.tint = 0xff8888; // Red tint for AI area
+ slotGraphics.tint = 0xff8888;
slot.x = AI_AREA_X + col * SLOT_WIDTH + SLOT_WIDTH / 2;
slot.y = AI_AREA_Y + row * SLOT_HEIGHT + SLOT_HEIGHT / 2;
gameLayer.addChild(slot);
}
}
- // Draw player deal area slots (hand)
+ // Player hand slots
for (var i = 0; i < 5; i++) {
var dealSlot = new Container();
var dealSlotGraphics = dealSlot.attachAsset('dealSlot', {
anchorX: 0.5,
anchorY: 0.5
});
dealSlotGraphics.alpha = 0.5;
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
- dealSlot.x = handStartX + i * DEAL_SLOT_WIDTH + i * 30 + DEAL_SLOT_WIDTH / 2;
- dealSlot.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
+ var slotPos = getHandSlotPosition(i);
+ dealSlot.x = slotPos.x;
+ dealSlot.y = slotPos.y;
gameLayer.addChild(dealSlot);
}
}
-function drawPaths() {
- // Path lines removed - enemies still follow invisible paths
-}
-// drawPathSegment function removed - no longer needed since path lines are removed
function getSlotPosition(row, col, isPlayerArea) {
var baseX = isPlayerArea ? PLAYER_AREA_X : AI_AREA_X;
var baseY = isPlayerArea ? PLAYER_AREA_Y : AI_AREA_Y;
return {
@@ -2675,13 +2244,12 @@
}
}
// Check player hand area
if (y >= PLAYER_DEAL_AREA_Y && y <= PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT) {
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
for (var i = 0; i < 5; i++) {
- var slotXStart = handStartX + i * (DEAL_SLOT_WIDTH + 30);
- var slotXEnd = slotXStart + DEAL_SLOT_WIDTH;
+ var slotPos = getHandSlotPosition(i);
+ var slotXStart = slotPos.x - DEAL_SLOT_WIDTH / 2;
+ var slotXEnd = slotPos.x + DEAL_SLOT_WIDTH / 2;
if (x >= slotXStart && x <= slotXEnd) {
return {
area: 'hand',
index: i
@@ -2700,11 +2268,9 @@
function evaluateRowHand(row, isPlayerArea) {
var playArea = isPlayerArea ? gameState.playerPlayArea : gameState.aiPlayArea;
var cards = [];
for (var col = 0; col < PLAY_AREA_COLS; col++) {
- if (playArea[row][col]) {
- cards.push(playArea[row][col]);
- }
+ if (playArea[row][col]) cards.push(playArea[row][col]);
}
return CardSystem.evaluatePokerHand(cards);
}
function updateHandNameDisplay(row, handEval) {
@@ -2731,16 +2297,14 @@
newText.y = PLAYER_AREA_Y + (row + 1) * SLOT_HEIGHT - 20;
uiLayer.addChild(newText);
playerHandNameTexts[row] = newText;
}
- } else {
- if (existingText) {
- existingText.visible = false;
- }
+ } else if (existingText) {
+ existingText.visible = false;
}
}
function applyHandBonuses() {
- // Apply bonuses to player cards
+ // Player cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
var handEval = evaluateRowHand(row, true);
updateHandNameDisplay(row, handEval);
var contributingCards = handEval.contributingCards || [];
@@ -2748,98 +2312,57 @@
var card = gameState.playerPlayArea[row][col];
if (card) {
card.handBonus = handEval.multiplier;
card.calculateStats();
- card.redOutline.visible = handEval.strength > 1 && contributingCards.indexOf(card) !== -1;
+ card.redOutline.visible = handEval.strength > 1 && contributingCards.includes(card);
}
}
}
- // Apply bonuses to AI cards
+ // AI cards
AISystem.applyAIHandBonuses();
}
/****
* AI System
****/
var AISystem = {
thinkTimer: 0,
thinkDelay: 60,
- // Think every second (60 ticks)
update: function update() {
this.thinkTimer++;
if (this.thinkTimer >= this.thinkDelay) {
this.thinkTimer = 0;
this.makeMove();
}
},
shouldDeal: function shouldDeal() {
- // Deal if we can afford it and have empty slots
- if (gameState.aiChips < gameState.dealCost) {
- return false;
- }
- var emptySlots = this.countEmptySlots();
- // Deal if there are any empty slots at all
- return emptySlots > 0;
+ if (gameState.aiChips < gameState.dealCost) return false;
+ return this.countEmptySlots() > 0;
},
countEmptySlots: function countEmptySlots() {
var count = 0;
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
- if (!gameState.aiPlayArea[row][col]) {
- count++;
- }
+ if (!gameState.aiPlayArea[row][col]) count++;
}
}
return count;
},
- countLowLevelCards: function countLowLevelCards() {
- var count = 0;
- for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- for (var col = 0; col < PLAY_AREA_COLS; col++) {
- var card = gameState.aiPlayArea[row][col];
- if (card && card.level <= 2) {
- count++;
- }
- }
- }
- return count;
- },
makeMove: function makeMove() {
- // Priority order: Merge > Place > Optimize > Deal. Only one action per cycle.
- if (this.tryMergeCards()) {
- // Successfully merged, wait for next think cycle
- return;
- }
- if (this.tryPlaceCards()) {
- // Successfully placed cards
- return;
- }
- if (this.optimizeCardPositions()) {
- // Successfully optimized, wait for next think cycle
- return;
- }
- // As a last resort, try to deal a new card if possible
- if (this.shouldDeal()) {
- this.dealAIHand();
- }
+ if (this.tryMergeCards()) return;
+ if (this.tryPlaceCards()) return;
+ if (this.optimizeCardPositions()) return;
+ if (this.shouldDeal()) this.dealAIHand();
},
tryMergeCards: function tryMergeCards() {
- // Look for mergeable cards
for (var row1 = 0; row1 < PLAY_AREA_ROWS; row1++) {
for (var col1 = 0; col1 < PLAY_AREA_COLS; col1++) {
var card1 = gameState.aiPlayArea[row1][col1];
- if (!card1) {
- continue;
- }
- // Look for a card to merge with
+ if (!card1) continue;
for (var row2 = 0; row2 < PLAY_AREA_ROWS; row2++) {
for (var col2 = 0; col2 < PLAY_AREA_COLS; col2++) {
- if (row1 === row2 && col1 === col2) {
- continue;
- }
+ if (row1 === row2 && col1 === col2) continue;
var card2 = gameState.aiPlayArea[row2][col2];
- if (!card2) {
- continue;
- }
+ if (!card2) continue;
if (card1.canMergeWith(card2)) {
this.mergeCards(card1, card2, row1, col1, row2, col2);
return true;
}
@@ -2850,42 +2373,31 @@
return false;
},
mergeCards: function mergeCards(card1, card2, row1, col1, row2, col2) {
var mergedCard = card1.mergeWith(card2);
- if (mergedCard) {
- // Remove old cards
- gameLayer.removeChild(card1);
- gameLayer.removeChild(card2);
- gameState.aiPlayArea[row1][col1] = null;
- gameState.aiPlayArea[row2][col2] = null;
- // Place merged card in the first position
- var pos = getSlotPosition(row1, col1, false);
- mergedCard.activate(pos.x, pos.y, true, false);
- gameLayer.addChild(mergedCard);
- gameState.aiPlayArea[row1][col1] = mergedCard;
- // Merge animation
- mergedCard.alpha = 0;
- mergedCard.scaleX = mergedCard.scaleY = 1.5;
- tween(mergedCard, {
- alpha: 1,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 200,
- easing: tween.elasticOut
- });
- createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
- this.applyAIHandBonuses();
- }
+ if (!mergedCard) return;
+ gameLayer.removeChild(card1);
+ gameLayer.removeChild(card2);
+ gameState.aiPlayArea[row1][col1] = null;
+ gameState.aiPlayArea[row2][col2] = null;
+ var pos = getSlotPosition(row1, col1, false);
+ mergedCard.activate(pos.x, pos.y, true, false);
+ gameLayer.addChild(mergedCard);
+ gameState.aiPlayArea[row1][col1] = mergedCard;
+ mergedCard.alpha = 0;
+ mergedCard.scale.set(1.5);
+ tween(mergedCard, {
+ alpha: 1,
+ scaleX: 1,
+ scaleY: 1
+ }, ANIMATION_CONFIGS.cardMerge);
+ createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
+ this.applyAIHandBonuses();
},
tryPlaceCards: function tryPlaceCards() {
- // AI doesn't have a "hand" like player, it deals directly to board
- // This function is for future expansion
- return false;
+ return false; // AI deals directly to board
},
optimizeCardPositions: function optimizeCardPositions() {
- // Move stronger cards to better positions (like completing poker hands)
- // For now, just try to complete rows for hand bonuses
return this.tryCompletePokerHands();
},
tryCompletePokerHands: function tryCompletePokerHands() {
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
@@ -2900,34 +2412,23 @@
} else {
emptyPositions.push(col);
}
}
- // If row is almost complete, try to fill it strategically
if (rowCards.length >= 3 && emptyPositions.length > 0) {
- // Look for cards in other rows that might complete a hand
if (this.tryMoveCardToCompleteHand(row, rowCards, emptyPositions[0])) {
- return true; // A move was made, so we are done for this 'think' cycle
+ return true;
}
}
}
- return false; // No move was made
+ return false;
},
tryMoveCardToCompleteHand: function tryMoveCardToCompleteHand(targetRow, existingCards, targetCol) {
- // Look for cards in other positions that might help complete a hand
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- if (row === targetRow) {
- continue;
- }
+ if (row === targetRow) continue;
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.aiPlayArea[row][col];
- if (!card) {
- continue;
- }
- // Simple heuristic: move cards of same suit or sequential values
- var shouldMove = this.cardHelpsHand(card, existingCards);
- if (shouldMove && Math.random() < 0.3) {
- // 30% chance to move
- // Move the card
+ if (!card) continue;
+ if (this.cardHelpsHand(card, existingCards) && Math.random() < 0.3) {
gameState.aiPlayArea[row][col] = null;
gameState.aiPlayArea[targetRow][targetCol] = card;
var newPos = getSlotPosition(targetRow, targetCol, false);
tween(card, {
@@ -2944,62 +2445,49 @@
}
return false;
},
cardHelpsHand: function cardHelpsHand(card, existingCards) {
- // Simple heuristic to see if a card might help complete a poker hand
var suits = {};
var values = {};
- existingCards.forEach(function (cardInfo) {
- var c = cardInfo.card.cardData;
- suits[c.suit] = (suits[c.suit] || 0) + 1;
- values[c.value] = (values[c.value] || 0) + 1;
+ existingCards.forEach(function (_ref3) {
+ var c = _ref3.card;
+ suits[c.cardData.suit] = (suits[c.cardData.suit] || 0) + 1;
+ values[c.cardData.value] = (values[c.cardData.value] || 0) + 1;
});
- // Check if card matches existing suits or values
- var cardSuit = card.cardData.suit;
- var cardValue = card.cardData.value;
- return suits[cardSuit] >= 2 || values[cardValue] >= 1;
+ return suits[card.cardData.suit] >= 2 || values[card.cardData.value] >= 1;
},
dealAIHand: function dealAIHand() {
- if (gameState.aiChips < gameState.dealCost) {
- return;
- }
+ if (gameState.aiChips < gameState.dealCost) return;
gameState.aiChips -= gameState.dealCost;
- // Deal one card to an empty slot
var cardData;
- // AI cannot play jokers, so re-deal if one is drawn.
do {
if (gameState.aiDeck.length === 0) {
gameState.aiDeck = CardSystem.createDeck();
}
cardData = gameState.aiDeck.pop();
} while (cardData.suit === 'joker');
var card = new Card(cardData);
- // After every 10 waves, new cards dealt start one level higher
var startLevel = Math.floor((WaveSystem.waveNumber - 1) / 10) + 1;
card.setLevel(startLevel);
- // Find best empty slot (prefer completing rows)
var bestSlot = this.findBestEmptySlot();
- if (bestSlot) {
- var pos = getSlotPosition(bestSlot.row, bestSlot.col, false);
- // Set initial off-screen position and properties for animation
- var startY = -SLOT_HEIGHT; // Come from top of the screen
- card.activate(pos.x, startY, true, false);
- card.rotation = Math.PI * 4; // Two full spins
- card.scale.set(0.1); // Start small
- gameLayer.addChild(card);
- gameState.aiPlayArea[bestSlot.row][bestSlot.col] = card;
- // Deal animation
- tween(card, {
- y: pos.y,
- rotation: 0,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 600,
- easing: tween.quadOut
- });
- this.applyAIHandBonuses();
- }
+ if (!bestSlot) return;
+ var pos = getSlotPosition(bestSlot.row, bestSlot.col, false);
+ var startY = -SLOT_HEIGHT;
+ card.activate(pos.x, startY, true, false);
+ card.rotation = Math.PI * 4;
+ card.scale.set(0.1);
+ gameLayer.addChild(card);
+ gameState.aiPlayArea[bestSlot.row][bestSlot.col] = card;
+ tween(card, {
+ y: pos.y,
+ rotation: 0,
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 600,
+ easing: tween.quadOut
+ });
+ this.applyAIHandBonuses();
},
findBestEmptySlot: function findBestEmptySlot() {
var emptySlots = [];
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
@@ -3013,43 +2501,34 @@
});
}
}
}
- if (emptySlots.length === 0) {
- return null;
- }
- // Sort by score (higher is better)
+ if (emptySlots.length === 0) return null;
emptySlots.sort(function (a, b) {
return b.score - a.score;
});
return emptySlots[0];
},
evaluateSlotScore: function evaluateSlotScore(row, col) {
var score = 0;
var cardsInRow = 0;
- // Count cards in this row
for (var c = 0; c < PLAY_AREA_COLS; c++) {
- if (gameState.aiPlayArea[row][c]) {
- cardsInRow++;
- }
+ if (gameState.aiPlayArea[row][c]) cardsInRow++;
}
- // Prefer completing rows
score += cardsInRow * 10;
- // Slight preference for middle positions
score += (2 - Math.abs(col - 2)) * 2;
return score;
},
applyAIHandBonuses: function applyAIHandBonuses() {
- // Apply poker hand bonuses to AI cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
var handEval = evaluateRowHand(row, false);
var contributingCards = handEval.contributingCards || [];
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.aiPlayArea[row][col];
if (card) {
card.handBonus = handEval.multiplier;
card.calculateStats();
- card.redOutline.visible = handEval.strength > 1 && contributingCards.indexOf(card) !== -1;
+ card.redOutline.visible = handEval.strength > 1 && contributingCards.includes(card);
}
}
}
}
@@ -3057,16 +2536,15 @@
/****
* Input Handling
****/
game.down = function (x, y, obj) {
- // Only handle input during playing state
- if (currentGameState !== 'playing') {
- return;
- }
- // Check if clicking on a card in hand
+ if (currentGameState !== 'playing') return;
+ // Check hand cards
for (var i = 0; i < gameState.playerHand.length; i++) {
var card = gameState.playerHand[i];
- if (card && Math.abs(x - card.x) < DEAL_SLOT_WIDTH / 2 && Math.abs(y - card.y) < DEAL_SLOT_HEIGHT / 2) {
+ if (!card) continue;
+ var slotPos = getHandSlotPosition(i);
+ if (Math.abs(x - slotPos.x) < DEAL_SLOT_WIDTH / 2 && Math.abs(y - slotPos.y) < DEAL_SLOT_HEIGHT / 2) {
selectedCard = card;
isDragging = true;
originalCardPosition = {
area: 'hand',
@@ -3075,311 +2553,249 @@
uiLayer.addChild(selectedCard);
return;
}
}
- // Check if clicking on a card in play area
+ // Check play area cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.playerPlayArea[row][col];
- if (card && Math.abs(x - card.x) < SLOT_WIDTH / 2 && Math.abs(y - card.y) < SLOT_HEIGHT / 2) {
+ if (!card) continue;
+ var pos = getSlotPosition(row, col, true);
+ if (Math.abs(x - pos.x) < SLOT_WIDTH / 2 && Math.abs(y - pos.y) < SLOT_HEIGHT / 2) {
selectedCard = card;
isDragging = true;
originalCardPosition = {
area: 'player',
row: row,
col: col
};
- // Remove from current position
gameState.playerPlayArea[row][col] = null;
gameLayer.addChild(selectedCard);
return;
}
}
}
};
game.move = function (x, y, obj) {
- // Only handle input during playing state
- if (currentGameState !== 'playing') {
- return;
- }
- if (isDragging && selectedCard) {
- selectedCard.x = x;
- selectedCard.y = y;
- // Highlight mergeable cards
- // Player's play area
- for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- for (var col = 0; col < PLAY_AREA_COLS; col++) {
- var card = gameState.playerPlayArea[row][col];
- if (card) {
- card.greenOutline.visible = selectedCard.canMergeWith(card);
- }
- }
- }
- // Player's hand
- for (var i = 0; i < gameState.playerHand.length; i++) {
- var card = gameState.playerHand[i];
- if (card && card !== selectedCard) {
+ if (currentGameState !== 'playing' || !isDragging || !selectedCard) return;
+ selectedCard.x = x;
+ selectedCard.y = y;
+ // Highlight mergeable cards
+ for (var row = 0; row < PLAY_AREA_ROWS; row++) {
+ for (var col = 0; col < PLAY_AREA_COLS; col++) {
+ var card = gameState.playerPlayArea[row][col];
+ if (card) {
card.greenOutline.visible = selectedCard.canMergeWith(card);
}
}
- // Switch to discard mode when dragging
- discardAreaGraphic.tint = 0x440000; // Dark red for discard
- // Highlight discard area on hover
- var isOverDiscard = discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2;
- if (isOverDiscard) {
- discardAreaGraphic.alpha = 1.0;
- discardText.setText('Sell Card');
- discardText.fill = 0xffd700; // Gold color
- } else {
- discardAreaGraphic.alpha = 0.7;
- discardText.setText('Discard');
- discardText.fill = 0x999999;
+ }
+ gameState.playerHand.forEach(function (card) {
+ if (card && card !== selectedCard) {
+ card.greenOutline.visible = selectedCard.canMergeWith(card);
}
+ });
+ // Update discard area
+ discardAreaGraphic.tint = 0x440000;
+ var isOverDiscard = discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2;
+ if (isOverDiscard) {
+ discardAreaGraphic.alpha = 1.0;
+ discardText.setText('Sell Card');
+ discardText.fill = 0xffd700;
} else {
- // When not dragging, show as deal button
- discardAreaGraphic.alpha = 1.0; // Reset to full alpha
- updateUI();
+ discardAreaGraphic.alpha = 0.7;
+ discardText.setText('Discard');
+ discardText.fill = 0x999999;
}
};
game.up = function (x, y, obj) {
- // Only handle input during playing state
- if (currentGameState !== 'playing') {
+ if (currentGameState !== 'playing' || !isDragging || !selectedCard) return;
+ isDragging = false;
+ // Clear outlines
+ [].concat(_toConsumableArray(gameState.playerPlayArea.flat()), _toConsumableArray(gameState.playerHand)).filter(function (card) {
+ return card;
+ }).forEach(function (card) {
+ return card.greenOutline.visible = false;
+ });
+ updateUI();
+ var targetSlot = getSlotFromPosition(x, y);
+ if (!targetSlot) {
+ returnCardToOriginalPosition();
+ selectedCard = null;
+ originalCardPosition = null;
+ applyHandBonuses();
return;
}
- if (isDragging && selectedCard) {
- isDragging = false;
- // Clear all temporary green outlines
- for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- for (var col = 0; col < PLAY_AREA_COLS; col++) {
- var card = gameState.playerPlayArea[row][col];
- if (card) {
- card.greenOutline.visible = false;
- }
- }
+ // Handle discard
+ if (targetSlot.area === 'discard') {
+ var chipRefund = 5 + selectedCard.level * 2;
+ gameState.playerChips += chipRefund;
+ if (originalCardPosition.area === 'hand') {
+ gameState.playerHand[originalCardPosition.index] = null;
}
- for (var i = 0; i < gameState.playerHand.length; i++) {
- var card = gameState.playerHand[i];
- if (card) {
- card.greenOutline.visible = false;
- }
- }
- // Reset to deal button mode
- updateUI();
- var targetSlot = getSlotFromPosition(x, y);
- if (targetSlot) {
- // Handle dropping card on discard area
- if (targetSlot.area === 'discard') {
- var chipRefund = 5 + selectedCard.level * 2;
- gameState.playerChips += chipRefund;
- // Remove from original position data
- if (originalCardPosition.area === 'hand') {
- gameState.playerHand[originalCardPosition.index] = null;
- } else if (originalCardPosition.area === 'player') {
- // This is already null from game.down, so this is just for safety.
- gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = null;
- }
- // Remove card graphic from scene and memory
- if (selectedCard.parent) {
- selectedCard.parent.removeChild(selectedCard);
- }
- createFloatingText('+' + formatNumberWithSuffix(chipRefund) + ' Chips', selectedCard.x, selectedCard.y, 0xffd700);
- selectedCard = null;
- originalCardPosition = null;
- updateUI();
- // Recalculate bonuses if a card was removed from play area
- if (originalCardPosition && originalCardPosition.area === 'player') {
- applyHandBonuses();
- }
- return; // Exit early
- }
- if (targetSlot.area === 'player') {
- var existingCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col];
- if (existingCard && selectedCard.canMergeWith(existingCard)) {
- // Merge in play area
- var mergedCard = selectedCard.mergeWith(existingCard);
- if (mergedCard) {
- gameLayer.removeChild(existingCard);
- var handIndex = gameState.playerHand.indexOf(selectedCard);
- if (handIndex !== -1) {
- uiLayer.removeChild(selectedCard);
- gameState.playerHand[handIndex] = null;
- } else {
- gameLayer.removeChild(selectedCard);
- }
- var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
- mergedCard.activate(pos.x, pos.y, true);
- gameLayer.addChild(mergedCard);
- gameState.playerPlayArea[targetSlot.row][targetSlot.col] = mergedCard;
- mergedCard.alpha = 0;
- mergedCard.scaleX = mergedCard.scaleY = 2;
- tween(mergedCard, {
- alpha: 1,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 300,
- easing: tween.elasticOut
- });
- createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
- }
- } else if (!existingCard) {
- // Prevent Jokers from being played on the board directly. They can only merge.
- if (selectedCard.cardData.suit === 'joker') {
- createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00);
- returnCardToOriginalPosition();
- } else {
- // Place in empty slot
- var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
- selectedCard.activate(pos.x, pos.y, true);
- var handIndex = gameState.playerHand.indexOf(selectedCard);
- if (handIndex !== -1) {
- uiLayer.removeChild(selectedCard);
- gameLayer.addChild(selectedCard);
- gameState.playerHand[handIndex] = null;
- }
- gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
- }
- } else {
- // Prevent Jokers from being swapped onto the board.
- if (selectedCard.cardData.suit === 'joker') {
- createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00);
- returnCardToOriginalPosition();
- } else {
- // Card exists, but cannot merge: swap them
- var swappedCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col];
- // Place selectedCard in target play area slot
- var pos1 = getSlotPosition(targetSlot.row, targetSlot.col, true);
- selectedCard.activate(pos1.x, pos1.y, true);
- var handIndex = gameState.playerHand.indexOf(selectedCard);
- if (handIndex !== -1) {
- uiLayer.removeChild(selectedCard);
- gameLayer.addChild(selectedCard);
- gameState.playerHand[handIndex] = null;
- }
- gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
- // Place swappedCard in original position
- if (originalCardPosition.area === 'player') {
- var pos2 = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
- swappedCard.activate(pos2.x, pos2.y, true);
- gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
- } else {
- // original was hand
- var origHandIndex = originalCardPosition.index;
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30;
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
- var slotX = handStartX + origHandIndex * DEAL_SLOT_WIDTH + origHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- swappedCard.activate(slotX, slotY, false, true);
- gameLayer.removeChild(swappedCard);
- uiLayer.addChild(swappedCard);
- gameState.playerHand[origHandIndex] = swappedCard;
- }
- }
- }
- } else if (targetSlot.area === 'hand') {
- var existingCard = gameState.playerHand[targetSlot.index];
- if (existingCard && existingCard !== selectedCard && selectedCard.canMergeWith(existingCard)) {
- // Merge in hand
- var mergedCard = selectedCard.mergeWith(existingCard);
- if (mergedCard) {
- // Remove old cards
- var handIndex1 = gameState.playerHand.indexOf(selectedCard);
- if (handIndex1 !== -1) {
- uiLayer.removeChild(selectedCard);
- gameState.playerHand[handIndex1] = null;
- } else {
- gameLayer.removeChild(selectedCard);
- }
- var handIndex2 = gameState.playerHand.indexOf(existingCard);
- if (handIndex2 !== -1) {
- uiLayer.removeChild(existingCard);
- gameState.playerHand[handIndex2] = null;
- }
- // Place merged card in hand
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
- var slotX = handStartX + targetSlot.index * DEAL_SLOT_WIDTH + targetSlot.index * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- mergedCard.activate(slotX, slotY, false, true);
- uiLayer.addChild(mergedCard);
- gameState.playerHand[targetSlot.index] = mergedCard;
- // Merge animation
- mergedCard.alpha = 0;
- mergedCard.scaleX = mergedCard.scaleY = 2;
- tween(mergedCard, {
- alpha: 1,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 300,
- easing: tween.elasticOut
- });
- createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
- } else {
- returnCardToOriginalPosition();
- }
- } else if (existingCard && existingCard !== selectedCard) {
- // Cannot merge, so swap
- var swappedCard = gameState.playerHand[targetSlot.index];
- // Move selectedCard to target hand slot
- var targetHandIndex = targetSlot.index;
- var handWidth1 = 5 * DEAL_SLOT_WIDTH + 4 * 30;
- var handStartX1 = (SCREEN_WIDTH - handWidth1) / 2;
- var slotX1 = handStartX1 + targetHandIndex * DEAL_SLOT_WIDTH + targetHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY1 = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- selectedCard.activate(slotX1, slotY1, false, true);
- if (originalCardPosition.area === 'player') {
- gameLayer.removeChild(selectedCard);
- uiLayer.addChild(selectedCard);
- } else {
- gameState.playerHand[originalCardPosition.index] = null;
- }
- gameState.playerHand[targetHandIndex] = selectedCard;
- // Move swappedCard to original position
- if (originalCardPosition.area === 'player') {
- var origPos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
- swappedCard.activate(origPos.x, origPos.y, true);
- uiLayer.removeChild(swappedCard);
- gameLayer.addChild(swappedCard);
- gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
- } else {
- // original was hand
- var origHandIndex = originalCardPosition.index;
- var handWidth2 = 5 * DEAL_SLOT_WIDTH + 4 * 30;
- var handStartX2 = (SCREEN_WIDTH - handWidth2) / 2;
- var slotX2 = handStartX2 + origHandIndex * DEAL_SLOT_WIDTH + origHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY2 = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- swappedCard.activate(slotX2, slotY2, false, true);
- gameState.playerHand[origHandIndex] = swappedCard;
- }
- } else {
- returnCardToOriginalPosition();
- }
- }
- } else {
- returnCardToOriginalPosition();
- }
+ if (selectedCard.parent) selectedCard.parent.removeChild(selectedCard);
+ createFloatingText('+' + formatNumberWithSuffix(chipRefund) + ' Chips', selectedCard.x, selectedCard.y, 0xffd700);
selectedCard = null;
originalCardPosition = null;
+ updateUI();
applyHandBonuses();
+ return;
}
+ // Handle placement
+ if (targetSlot.area === 'player') {
+ handlePlayAreaPlacement(targetSlot);
+ } else if (targetSlot.area === 'hand') {
+ handleHandPlacement(targetSlot);
+ }
+ selectedCard = null;
+ originalCardPosition = null;
+ applyHandBonuses();
};
-function returnCardToOriginalPosition() {
- if (!selectedCard || !originalCardPosition) {
- return;
+function handlePlayAreaPlacement(targetSlot) {
+ var existingCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col];
+ if (existingCard && selectedCard.canMergeWith(existingCard)) {
+ // Merge cards
+ var mergedCard = selectedCard.mergeWith(existingCard);
+ if (!mergedCard) return;
+ gameLayer.removeChild(existingCard);
+ var handIndex = gameState.playerHand.indexOf(selectedCard);
+ if (handIndex !== -1) {
+ uiLayer.removeChild(selectedCard);
+ gameState.playerHand[handIndex] = null;
+ } else {
+ gameLayer.removeChild(selectedCard);
+ }
+ var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
+ mergedCard.activate(pos.x, pos.y, true);
+ gameLayer.addChild(mergedCard);
+ gameState.playerPlayArea[targetSlot.row][targetSlot.col] = mergedCard;
+ mergedCard.alpha = 0;
+ mergedCard.scale.set(2);
+ tween(mergedCard, {
+ alpha: 1,
+ scaleX: 1,
+ scaleY: 1
+ }, ANIMATION_CONFIGS.cardMerge);
+ createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
+ } else if (!existingCard) {
+ // Place in empty slot
+ if (selectedCard.cardData.suit === 'joker') {
+ createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00);
+ returnCardToOriginalPosition();
+ return;
+ }
+ var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
+ selectedCard.activate(pos.x, pos.y, true);
+ var handIndex = gameState.playerHand.indexOf(selectedCard);
+ if (handIndex !== -1) {
+ uiLayer.removeChild(selectedCard);
+ gameLayer.addChild(selectedCard);
+ gameState.playerHand[handIndex] = null;
+ }
+ gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
+ } else {
+ // Swap cards
+ if (selectedCard.cardData.suit === 'joker') {
+ createFloatingText('Jokers can only level up other cards.', selectedCard.x, selectedCard.y, 0xffcc00);
+ returnCardToOriginalPosition();
+ return;
+ }
+ var swappedCard = existingCard;
+ // Place selected card
+ var pos1 = getSlotPosition(targetSlot.row, targetSlot.col, true);
+ selectedCard.activate(pos1.x, pos1.y, true);
+ var handIndex = gameState.playerHand.indexOf(selectedCard);
+ if (handIndex !== -1) {
+ uiLayer.removeChild(selectedCard);
+ gameLayer.addChild(selectedCard);
+ gameState.playerHand[handIndex] = null;
+ }
+ gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
+ // Place swapped card back
+ if (originalCardPosition.area === 'player') {
+ var pos2 = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
+ swappedCard.activate(pos2.x, pos2.y, true);
+ gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
+ } else {
+ var slotPos = getHandSlotPosition(originalCardPosition.index);
+ swappedCard.activate(slotPos.x, slotPos.y, false, true);
+ gameLayer.removeChild(swappedCard);
+ uiLayer.addChild(swappedCard);
+ gameState.playerHand[originalCardPosition.index] = swappedCard;
+ }
}
+}
+function handleHandPlacement(targetSlot) {
+ var existingCard = gameState.playerHand[targetSlot.index];
+ if (existingCard && existingCard !== selectedCard && selectedCard.canMergeWith(existingCard)) {
+ // Merge in hand
+ var mergedCard = selectedCard.mergeWith(existingCard);
+ if (!mergedCard) {
+ returnCardToOriginalPosition();
+ return;
+ }
+ // Remove old cards
+ var handIndex1 = gameState.playerHand.indexOf(selectedCard);
+ if (handIndex1 !== -1) {
+ uiLayer.removeChild(selectedCard);
+ gameState.playerHand[handIndex1] = null;
+ } else {
+ gameLayer.removeChild(selectedCard);
+ }
+ var handIndex2 = gameState.playerHand.indexOf(existingCard);
+ if (handIndex2 !== -1) {
+ uiLayer.removeChild(existingCard);
+ gameState.playerHand[handIndex2] = null;
+ }
+ // Place merged card
+ var slotPos = getHandSlotPosition(targetSlot.index);
+ mergedCard.activate(slotPos.x, slotPos.y, false, true);
+ uiLayer.addChild(mergedCard);
+ gameState.playerHand[targetSlot.index] = mergedCard;
+ mergedCard.alpha = 0;
+ mergedCard.scale.set(2);
+ tween(mergedCard, {
+ alpha: 1,
+ scaleX: 1,
+ scaleY: 1
+ }, ANIMATION_CONFIGS.cardMerge);
+ createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
+ } else if (existingCard && existingCard !== selectedCard) {
+ // Swap cards
+ var swappedCard = existingCard;
+ // Move selected card to target slot
+ var slotPos1 = getHandSlotPosition(targetSlot.index);
+ selectedCard.activate(slotPos1.x, slotPos1.y, false, true);
+ if (originalCardPosition.area === 'player') {
+ gameLayer.removeChild(selectedCard);
+ uiLayer.addChild(selectedCard);
+ } else {
+ gameState.playerHand[originalCardPosition.index] = null;
+ }
+ gameState.playerHand[targetSlot.index] = selectedCard;
+ // Move swapped card to original position
+ if (originalCardPosition.area === 'player') {
+ var origPos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
+ swappedCard.activate(origPos.x, origPos.y, true);
+ uiLayer.removeChild(swappedCard);
+ gameLayer.addChild(swappedCard);
+ gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
+ } else {
+ var slotPos2 = getHandSlotPosition(originalCardPosition.index);
+ swappedCard.activate(slotPos2.x, slotPos2.y, false, true);
+ gameState.playerHand[originalCardPosition.index] = swappedCard;
+ }
+ } else {
+ returnCardToOriginalPosition();
+ }
+}
+function returnCardToOriginalPosition() {
+ if (!selectedCard || !originalCardPosition) return;
if (originalCardPosition.area === 'hand') {
- // Return to hand slot
- var handIndex = originalCardPosition.index;
- var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
- var handStartX = (SCREEN_WIDTH - handWidth) / 2;
- var slotX = handStartX + handIndex * DEAL_SLOT_WIDTH + handIndex * 30 + DEAL_SLOT_WIDTH / 2;
- var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
- selectedCard.x = slotX;
- selectedCard.y = slotY;
- gameState.playerHand[handIndex] = selectedCard;
+ var slotPos = getHandSlotPosition(originalCardPosition.index);
+ selectedCard.x = slotPos.x;
+ selectedCard.y = slotPos.y;
+ gameState.playerHand[originalCardPosition.index] = selectedCard;
} else if (originalCardPosition.area === 'player') {
- // Return to play area slot
var pos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
selectedCard.x = pos.x;
selectedCard.y = pos.y;
gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = selectedCard;
@@ -3392,20 +2808,18 @@
weight: 800,
stroke: 0x000000,
strokeThickness: 0
};
- // Add black outline for green level up text
if (color === 0x00ff00) {
textOptions.strokeThickness = 6;
- textOptions.size = (size || 40) * 2; // Make it twice as big
+ textOptions.size = (size || 40) * 2;
}
var floatingText = new Text2(text, textOptions);
floatingText.anchor.set(0.5, 0.5);
floatingText.x = x;
floatingText.y = y;
floatingText.alpha = 1;
uiLayer.addChild(floatingText);
- // Use larger animation for important messages
var animationDistance = size && size > 40 ? 120 : 80;
var animationDuration = size && size > 40 ? 2000 : 1500;
tween(floatingText, {
y: y - animationDistance,
@@ -3413,106 +2827,67 @@
}, {
duration: animationDuration,
easing: tween.quadOut,
onFinish: function onFinish() {
- uiLayer.removeChild(floatingText);
+ return uiLayer.removeChild(floatingText);
}
});
}
-function createExplosion(x, y, color) {
- var numParticles = 5;
- for (var i = 0; i < numParticles; i++) {
- var particle = LK.getAsset('explosionParticle', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- particle.x = x;
- particle.y = y;
- particle.tint = color;
- particle.scale.set(0.5 + Math.random() * 0.5);
- var angle = Math.random() * Math.PI * 2;
- var distance = Math.random() * 50 + 20;
- var duration = 500 + Math.random() * 500;
- var targetX = x + Math.cos(angle) * distance;
- var targetY = y + Math.sin(angle) * distance;
- gameLayer.addChild(particle);
- tween(particle, {
- x: targetX,
- y: targetY,
- alpha: 0
- }, {
- duration: duration,
- easing: tween.quadOut,
- onFinish: function (p) {
- return function () {
- if (p.parent) {
- p.parent.removeChild(p);
- }
- };
- }(particle)
- });
- }
-}
/****
* Main Game Loop
****/
game.update = function () {
- // Only run game logic when in playing state
if (currentGameState !== 'playing') {
if (currentGameState === 'start' && backgroundSuits.length > 0) {
var speed = 0.5;
var spacing = 400;
- // Calculate pattern dimensions
var numCols = Math.ceil(SCREEN_WIDTH / spacing) + 3;
var numRows = Math.ceil(SCREEN_HEIGHT / spacing) + 3;
var patternWidth = numCols * spacing;
var patternHeight = numRows * spacing;
- for (var i = 0; i < backgroundSuits.length; i++) {
- var suit = backgroundSuits[i];
- // Move diagonally
+ backgroundSuits.forEach(function (suit) {
suit.x += speed;
suit.y += speed;
- // Wrap horizontally
if (suit.x > SCREEN_WIDTH + spacing * 1.5) {
suit.x -= patternWidth;
}
- // Wrap vertically
if (suit.y > SCREEN_HEIGHT + spacing * 1.5) {
suit.y -= patternHeight;
}
- }
+ });
}
return;
}
- // Clean up dead chips immediately
- cleanupDeadChips();
- // Update wave spawning system
+ // Clean up dead chips
+ activePlayerChips = activePlayerChips.filter(function (chip) {
+ return chip.active && chip.health > 0;
+ });
+ activeAIChips = activeAIChips.filter(function (chip) {
+ return chip.active && chip.health > 0;
+ });
+ // Update systems
WaveSystem.update();
- // Update active chips
- for (var i = activePlayerChips.length - 1; i >= 0; i--) {
- activePlayerChips[i].update();
- }
- for (var i = activeAIChips.length - 1; i >= 0; i--) {
- activeAIChips[i].update();
- }
- // Update active bullets
- for (var i = activeBullets.length - 1; i >= 0; i--) {
- activeBullets[i].update();
- }
- // Update cards in play
+ // Update entities
+ activePlayerChips.forEach(function (chip) {
+ return chip.update();
+ });
+ activeAIChips.forEach(function (chip) {
+ return chip.update();
+ });
+ activeBullets.forEach(function (bullet) {
+ return bullet.update();
+ });
+ // Update cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
- if (gameState.playerPlayArea[row][col]) {
- gameState.playerPlayArea[row][col].update();
- }
- if (gameState.aiPlayArea[row][col]) {
- gameState.aiPlayArea[row][col].update();
- }
+ var playerCard = gameState.playerPlayArea[row][col];
+ var aiCard = gameState.aiPlayArea[row][col];
+ if (playerCard) playerCard.update();
+ if (aiCard) aiCard.update();
}
}
// Update AI
AISystem.update();
- // Check win/lose conditions
// Update life displays
while (gameState.playerLives < lastPlayerLives) {
lastPlayerLives--;
var heartToFade = playerLifeHearts[lastPlayerLives];
@@ -3534,8 +2909,9 @@
duration: 500
});
}
}
+ // Check win/lose
if (gameState.playerLives <= 0) {
showGameOver(false);
} else if (gameState.aiLives <= 0) {
showGameOver(true);
@@ -3551,34 +2927,8 @@
} else {
LK.showGameOver();
}
}
-/****
-* Utility Functions
-****/
-function formatNumberWithSuffix(number) {
- var num = Math.round(number);
- if (num < 1000) {
- return num.toString();
- }
- if (num < 1000000) {
- return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'K';
- }
- if (num < 1000000000) {
- return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
- }
- if (num < 1000000000000) {
- return (num / 1000000000).toFixed(1).replace(/\.0$/, '') + 'B';
- }
- return (num / 1000000000000).toFixed(1).replace(/\.0$/, '') + 'T';
-}
-function displayHandInfo() {
- // Show current poker hand evaluations for debugging
- for (var row = 0; row < PLAY_AREA_ROWS; row++) {
- var handEval = evaluateRowHand(row, true);
- console.log('Player Row ' + row + ':', handEval.type, 'Multiplier:', handEval.multiplier);
- }
-}
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0
});
@@ -3586,29 +2936,29 @@
background.y = 50;
background.visible = false;
gameLayer.addChild(background);
gameElements.push(background);
-// Place two chipracks side by side in the center at the top of the screen
+// Chip racks
var chipRack1 = LK.getAsset('chipRack', {
anchorX: 0.5,
anchorY: 0
});
var rackWidth = chipRack1.width;
-var rackHeight = chipRack1.height;
chipRack1.x = SCREEN_WIDTH / 2 - rackWidth / 2;
-chipRack1.y = 60 - rackHeight * 0.75 + 30;
+chipRack1.y = 60 - chipRack1.height * 0.75 + 30;
chipRack1.visible = false;
gameLayer.addChild(chipRack1);
gameElements.push(chipRack1);
var chipRack2 = LK.getAsset('chipRack', {
anchorX: 0.5,
anchorY: 0
});
chipRack2.x = SCREEN_WIDTH / 2 + rackWidth / 2;
-chipRack2.y = 60 - rackHeight * 0.75 + 30;
+chipRack2.y = 60 - chipRack2.height * 0.75 + 30;
chipRack2.visible = false;
gameLayer.addChild(chipRack2);
gameElements.push(chipRack2);
+// Border
var border = LK.getAsset('border', {
anchorX: 0.5,
anchorY: 0.5
});
@@ -3616,8 +2966,9 @@
border.y = SCREEN_HEIGHT / 2;
border.visible = false;
gameLayer.addChild(border);
gameElements.push(border);
+// Bottom bar
var bottomBar = LK.getAsset('bottomBar', {
anchorX: 0.5,
anchorY: 1
});
@@ -3625,14 +2976,9 @@
bottomBar.y = SCREEN_HEIGHT;
bottomBar.visible = false;
gameLayer.addChild(bottomBar);
gameElements.push(bottomBar);
-// Show start screen instead of initializing game immediately
-createStartScreen();
-// Add active chips container below the bottom bar
-gameLayer.addChildAt(activePlayerChipsContainer, gameLayer.getChildIndex(bottomBar));
-gameLayer.addChildAt(activeAIChipsContainer, gameLayer.getChildIndex(bottomBar));
-// Add chipStack asset above chips display
+// Chip stack
var chipStack = LK.getAsset('chipStack', {
anchorX: 0.5,
anchorY: 1
});
@@ -3640,6 +2986,9 @@
chipStack.y = playerChipsText.y - 10;
chipStack.visible = false;
uiLayer.addChild(chipStack);
gameElements.push(chipStack);
-;
-;
\ No newline at end of file
+// Initialize containers
+gameLayer.addChildAt(activePlayerChipsContainer, gameLayer.getChildIndex(bottomBar));
+gameLayer.addChildAt(activeAIChipsContainer, gameLayer.getChildIndex(bottomBar));
+// Start game
+createStartScreen();
\ No newline at end of file
A long rack of different colored poker chips seen from above. Anime style.. In-Game asset. 2d. High contrast. No shadows
A graphic for the center of a joker card.
a 2:3 format thin black border with nothing in the center. In-Game asset. 2d. High contrast. No shadows
A small white explosion particle.. In-Game asset. 2d. High contrast. No shadows
Make the blue a lighter blue.
Make this in a white instead of blue. Keep everything else the same.
A couple different sized stacks of these chips beside each other.
Just the spade from this picture with a blue snowflake in the middle of it.
Just the heart from this picture with a flame in the cent t of it.
Just the club from this picture with 1. **Fan/Spray Symbol** - Three or more lines radiating outward from a central point, yellow in color, in the center of the club.
Just the diamond from this picture with a dollar sign in the center
A white circle with a lightening gradient towards the edge.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A simple golden line break.. In-Game asset. 2d. High contrast. No shadows
A fanned card hand that shows a royal flush in spades. Anime style. In-Game asset. 2d. High contrast. No shadows
An SVG of the word 'Battle'. text in yellow with a black outline. In-Game asset. 2d. High contrast. No shadows
change the text to say "Mods"
The four card suits arranged in 2x2 grid layout, no lines. Anime style. In-Game asset. 2d. High contrast. No shadows
A single ice crystal. anime style. In-Game asset. 2d. High contrast. No shadows
Change the text to say ‘Refund’. Change the cards to a trash can.
A completely blank playing card with textured surface. Slightly used edges with a couple nicks out of it. Black background. In-Game asset. 2d. High contrast. No shadows
A 3:2 ratio rectangular green button that says “PvP” using this yellow font.
Change the text to say ‘Co-op’
Change the font to say ‘Victory!’
Change the text to say ‘Defeat!’
A 2:3 ratio rectangular picture that shows two card playing cats in a casino very close face to face with teeth bared and fists clenched as if they’re about to fight. Each cat has a different card suit pattern on the fur of their forehead. One is wearing a suit and the other is wearing tan leather jacket with a striped tank top underneath. Anime style.. In-Game asset. 2d. High contrast. No shadows
Show these same cats smiling and instead of clenched fists they’re grasping hands because they’re friends.
Incorporate these two cats heads into a game logo for a poker based tower defense that includes the name “Double Down Defense”. Put their heads offset on either side with eyes open and looking at the logo.
A small treasure chest with poker themed graphics on it. Anime style. In-Game asset. 2d. High contrast. No shadows
The hearts card suit symbol with two linked hearts in the center of it. Anime style.. In-Game asset. 2d. High contrast. No shadows
The diamond card suit with a coin in the center. The coin has a ‘2X’ in the center. Anime style.. In-Game asset. 2d. High contrast. No shadows
Just the club from this picture with a clock in the center.
Just the spade from this image with a land mine in the center of it.
Just the mine from this image.
Just the heart from this image with a piggy bank in the center.
Just the diamond from this picture with a sword with a small arrow pointing up in the center of the diamond.
Just the club from this picture with an icon in the center of it that represents a projectile bouncing at an angle off of a surface.
Just the spade with a skull in the center of it. Anime style.
This chest with the top open and nothing inside.
Change the text to say Shop
An old style cash register. The numeric read out says 7.77. Anime style.. In-Game asset. 2d. High contrast. No shadows
A giant question mark. Anime style.. In-Game asset. 2d. High contrast. No shadows
A shield with a spade and heart card suit coat of arms on it with a sword crossed downwards, behind it. icon. Anime style.. In-Game asset. 2d. High contrast. No shadows
Change the text to say ‘Draw’
The back of a playing card. Blue pattern. Anime style.. In-Game asset. 2d. High contrast. No shadows
The back of a playing card. Red pattern with a heart in the center. Anime style.. In-Game asset. 2d. High contrast. No shadows