Code edit (1 edits merged)
Please save this source code
User prompt
Put a dark overlay using the card asset below the four cards at the top of the mod screen on the mod screen. Be big enough for all the cards and low enough to include the currently equipped mods text.
User prompt
Put a dark overlay using the card asset below the currently equipped mods at the top of the screen on the mod screen.
User prompt
Put a dark overlay using the card asset below the currently equipped mods. 3:2 ratio that is big enough for all the mods.
User prompt
Add white text underneath the currently equipped mods at the top of the mod screen that says “Currently equipped mods”
User prompt
Lower the position of the tap message on the title screen, make the font bigger and an add a slow flashing to it. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Do not show the bottomBar when the game first starts. Instead, when the title logo animation finishes add a “Tap anywhere to start” message underneath the logo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add an animation to the title logo where it starts at a super small scale and scales to full size. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the game first starts add an expanding animation to the title logo. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Round up fractional returns from the greed mod.
User prompt
When the greed mod is equipped. Each diamond on board adds an additional 10% chip gain for each card level it has. This is on top of the base gain the greed mod already provides. Do not return fractional gains.
User prompt
When the greed mod is equipped. Each diamond on board adds an additional 10% chip gain for each card level it has. Do not return fractional gains.
User prompt
When dragging a card to an occupied slot triggers a trade of card slots, add a quick animation to show the two cards switching spots. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
The refund button just says refund instead of displaying the amount that will be refunded like it should.
User prompt
When the refund is offered use the refundButton with + followed by the refund price that will be gained at the bottom in the same place that the deal price is located on the deal button.
User prompt
Increase refund to half of the current deal cost.
User prompt
make sure bosses dont get permenantly frozen by giving them periods of freezing immunity ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
when new poker chips are spawned, place them on a layer lower than the previous chip
User prompt
analyze code, look for the most poorly performing sections or memory leaks and refactor for efficiency and performance without losing ANY functionality
User prompt
analyze code and refactor for efficiency and performance without losing ANY functionality
User prompt
Please fix the bug: 'TypeError: floatingText.style is undefined' in or related to this line: 'floatingText.style.fontSize = size || 40;' Line Number: 3107
User prompt
experience a lot of slowdown, analyze pool sizes and animation methods. look for memory leaks
User prompt
still experiencing a lot of performance issues. analyze and provide any refactoring as needed
User prompt
im experience a lot of slowdown with bullets. analyze pool system and optimize
/**** * 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: [], nextChipIndex: 0, bulletPool: [], nextBulletIndex: 0, CHIP_POOL_SIZE: 50, BULLET_POOL_SIZE: 100, init: function init() { this.nextChipIndex = 0; this.nextBulletIndex = 0; if (this.chipPool.length === 0) { for (var i = 0; i < this.CHIP_POOL_SIZE; i++) { var chip = new PokerChip(); chip.active = false; chip.visible = false; this.chipPool.push(chip); } } if (this.bulletPool.length === 0) { 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() { for (var i = 0; i < this.CHIP_POOL_SIZE; i++) { var index = (this.nextChipIndex + i) % this.CHIP_POOL_SIZE; var chip = this.chipPool[index]; if (chip && !chip.active) { this.nextChipIndex = (index + 1) % this.CHIP_POOL_SIZE; return chip; } } return null; }, getBullet: function getBullet() { for (var i = 0; i < this.BULLET_POOL_SIZE; i++) { var index = (this.nextBulletIndex + i) % this.BULLET_POOL_SIZE; var bullet = this.bulletPool[index]; if (bullet && !bullet.active) { this.nextBulletIndex = (index + 1) % this.BULLET_POOL_SIZE; return bullet; } } return 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 var index = activePlayerChips.indexOf(chip); if (index > -1) { activePlayerChips.splice(index, 1); } else { index = activeAIChips.indexOf(chip); if (index > -1) { activeAIChips.splice(index, 1); } } // 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; var index = activeBullets.indexOf(bullet); if (index > -1) { activeBullets.splice(index, 1); } 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();
/****
* 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: [],
nextChipIndex: 0,
bulletPool: [],
nextBulletIndex: 0,
CHIP_POOL_SIZE: 50,
BULLET_POOL_SIZE: 100,
init: function init() {
this.nextChipIndex = 0;
this.nextBulletIndex = 0;
if (this.chipPool.length === 0) {
for (var i = 0; i < this.CHIP_POOL_SIZE; i++) {
var chip = new PokerChip();
chip.active = false;
chip.visible = false;
this.chipPool.push(chip);
}
}
if (this.bulletPool.length === 0) {
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() {
for (var i = 0; i < this.CHIP_POOL_SIZE; i++) {
var index = (this.nextChipIndex + i) % this.CHIP_POOL_SIZE;
var chip = this.chipPool[index];
if (chip && !chip.active) {
this.nextChipIndex = (index + 1) % this.CHIP_POOL_SIZE;
return chip;
}
}
return null;
},
getBullet: function getBullet() {
for (var i = 0; i < this.BULLET_POOL_SIZE; i++) {
var index = (this.nextBulletIndex + i) % this.BULLET_POOL_SIZE;
var bullet = this.bulletPool[index];
if (bullet && !bullet.active) {
this.nextBulletIndex = (index + 1) % this.BULLET_POOL_SIZE;
return bullet;
}
}
return 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
var index = activePlayerChips.indexOf(chip);
if (index > -1) {
activePlayerChips.splice(index, 1);
} else {
index = activeAIChips.indexOf(chip);
if (index > -1) {
activeAIChips.splice(index, 1);
}
}
// 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;
var index = activeBullets.indexOf(bullet);
if (index > -1) {
activeBullets.splice(index, 1);
}
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();
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