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) { 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 hitRadiusSq = 85 * 85; // Use squared radius for performance var distanceSq = dx * dx + dy * dy; // Since bullet speed is much smaller than hit radius, a simple distance check is sufficient and more performant. if (distanceSq < hitRadiusSq) { 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 { // Move towards target var distance = Math.sqrt(distanceSq); self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } 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; } // Chip cleanup is handled by the PoolManager when chips are returned to the pool. // The filter here is redundant and causes performance issues due to array recreation every frame. // Update systems WaveSystem.update(); // Update entities activePlayerChips.forEach(function (chip) { return chip.update(); }); activeAIChips.forEach(function (chip) { return chip.update(); }); activeBullets.forEach(function (bullet) { return bullet.update(); }); // Update cards for (var row = 0; row < PLAY_AREA_ROWS; row++) { for (var col = 0; col < PLAY_AREA_COLS; col++) { var playerCard = gameState.playerPlayArea[row][col]; var aiCard = gameState.aiPlayArea[row][col]; if (playerCard) playerCard.update(); if (aiCard) aiCard.update(); } } // Update AI AISystem.update(); // Update life displays while (gameState.playerLives < lastPlayerLives) { lastPlayerLives--; var heartToFade = playerLifeHearts[lastPlayerLives]; if (heartToFade) { tween(heartToFade, { alpha: 0.2 }, { duration: 500 }); } } while (gameState.aiLives < lastAiLives) { lastAiLives--; var heartToFade = aiLifeHearts[lastAiLives]; if (heartToFade) { tween(heartToFade, { alpha: 0.2 }, { duration: 500 }); } } // Check win/lose if (gameState.playerLives <= 0) { showGameOver(false); } else if (gameState.aiLives <= 0) { showGameOver(true); } updateUI(); }; /**** * Game Over ****/ function showGameOver(playerWon) { if (playerWon) { LK.showYouWin(); } else { LK.showGameOver(); } } var background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0 }); background.x = SCREEN_WIDTH / 2; background.y = 50; background.visible = false; gameLayer.addChild(background); gameElements.push(background); // Chip racks var chipRack1 = LK.getAsset('chipRack', { anchorX: 0.5, anchorY: 0 }); var rackWidth = chipRack1.width; chipRack1.x = SCREEN_WIDTH / 2 - rackWidth / 2; chipRack1.y = 60 - chipRack1.height * 0.75 + 30; chipRack1.visible = false; gameLayer.addChild(chipRack1); gameElements.push(chipRack1); var chipRack2 = LK.getAsset('chipRack', { anchorX: 0.5, anchorY: 0 }); chipRack2.x = SCREEN_WIDTH / 2 + rackWidth / 2; chipRack2.y = 60 - chipRack2.height * 0.75 + 30; chipRack2.visible = false; gameLayer.addChild(chipRack2); gameElements.push(chipRack2); // Border var border = LK.getAsset('border', { anchorX: 0.5, anchorY: 0.5 }); border.x = SCREEN_WIDTH / 2; border.y = SCREEN_HEIGHT / 2; border.visible = false; gameLayer.addChild(border); gameElements.push(border); // Bottom bar var bottomBar = LK.getAsset('bottomBar', { anchorX: 0.5, anchorY: 1 }); bottomBar.x = SCREEN_WIDTH / 2; bottomBar.y = SCREEN_HEIGHT; bottomBar.visible = false; gameLayer.addChild(bottomBar); gameElements.push(bottomBar); // Chip stack var chipStack = LK.getAsset('chipStack', { anchorX: 0.5, anchorY: 1 }); chipStack.x = playerChipsText.x + playerChipsText.width / 2; chipStack.y = playerChipsText.y - 10; chipStack.visible = false; uiLayer.addChild(chipStack); gameElements.push(chipStack); // Initialize containers gameLayer.addChildAt(activePlayerChipsContainer, gameLayer.getChildIndex(bottomBar)); gameLayer.addChildAt(activeAIChipsContainer, gameLayer.getChildIndex(bottomBar)); // Start game createStartScreen();
===================================================================
--- original.js
+++ change.js
@@ -94,20 +94,16 @@
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) {
+ var hitRadiusSq = 85 * 85; // Use squared radius for performance
+ var distanceSq = dx * dx + dy * dy;
+ // Since bullet speed is much smaller than hit radius, a simple distance check is sufficient and more performant.
+ if (distanceSq < hitRadiusSq) {
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;
@@ -119,10 +115,12 @@
}
if (currentGraphic) currentGraphic.visible = false;
PoolManager.returnBullet(self);
} else {
- self.x = newX;
- self.y = newY;
+ // Move towards target
+ var distance = Math.sqrt(distanceSq);
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
}
} else {
if (currentGraphic) currentGraphic.visible = false;
PoolManager.returnBullet(self);
@@ -2880,15 +2878,10 @@
});
}
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;
- });
+ // Chip cleanup is handled by the PoolManager when chips are returned to the pool.
+ // The filter here is redundant and causes performance issues due to array recreation every frame.
// Update systems
WaveSystem.update();
// Update entities
activePlayerChips.forEach(function (chip) {
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