User prompt
Place the floorbackground as a background image below all other layers.
User prompt
Place the floorbackground as a background image underneath all other layers.
User prompt
Place the floorbackground as a background image below all other layers.
User prompt
Shift the background down 50 pixels.
User prompt
Place floorbackground centered as the background below everything else.
User prompt
Reduce gold gain
User prompt
Increase boss power by 10 and increase chip size to fit health numbers.
User prompt
reduce wave 1 difficulty slightly and slightly increase difficulty of waves starting at 2 onwards
User prompt
combine deal button and discard area. discard area becomes deal button, unless you're dragging a card, then discard becomes visible. keep on the right side
User prompt
slow down 1 value chips
User prompt
update as needed with: var WaveSystem = { // ... existing properties waveDuration: 2400, // Increased from 1800 (40 seconds vs 30) getSpawnInterval: function(waveNumber) { // Start slower, ramp up gradually if (waveNumber <= 3) return 120; // 2 seconds if (waveNumber <= 6) return 90; // 1.5 seconds if (waveNumber <= 10) return 60; // 1 second if (waveNumber <= 15) return 45; // 0.75 seconds return Math.max(30, 60 - waveNumber); // Min 0.5 seconds }, getChipValue: function(waveNumber) { // More gradual progression if (waveNumber === 1) return 1; else if (waveNumber <= 3) { return Math.random() < 0.05 ? 5 : 1; // 5% chance } else if (waveNumber <= 5) { return Math.random() < 0.15 ? 5 : 1; // 15% chance } else if (waveNumber <= 8) { return Math.random() < 0.3 ? 5 : 1; // 30% chance } else if (waveNumber <= 12) { var rand = Math.random(); if (rand < 0.05) return 10; // 5% chance else if (rand < 0.4) return 5; // 35% chance else return 1; } else if (waveNumber <= 18) { var rand = Math.random(); if (rand < 0.1) return 10; // 10% chance else if (rand < 0.5) return 5; // 40% chance else return 1; } else if (waveNumber <= 25) { var rand = Math.random(); if (rand < 0.02) return 25; // 2% chance else if (rand < 0.15) return 10; // 13% chance else if (rand < 0.6) return 5; // 45% chance else return 1; } else { // Wave 26+ var rand = Math.random(); if (rand < 0.05) return 25; // 5% chance else if (rand < 0.2) return 10; // 15% chance else if (rand < 0.7) return 5; // 50% chance else return 1; } }, getBossChipValue: function(waveNumber) { // More reasonable boss scaling var bossWave = Math.floor(waveNumber / 10); var baseHealth = 500; return baseHealth + (bossWave - 1) * 250; // 500, 750, 1000, 1250... }, getChipCount: function(waveNumber) { if (this.isBossWave(waveNumber)) return 1; // Increase multi-spawns in later waves var rand = Math.random(); if (waveNumber >= 15) { if (rand < 0.1) return 4; // 10% quad spawn else if (rand < 0.25) return 3; // 15% triple else if (rand < 0.5) return 2; // 25% double } else if (waveNumber >= 8) { if (rand < 0.2) return 3; // 20% triple else if (rand < 0.4) return 2; // 20% double } else if (waveNumber >= 4) { if (rand < 0.25) return 2; // 25% double } return 1; } };
User prompt
update as needed with: // In PokerChip activate() // More challenging base health that scales better self.maxHealth = 50 + (value * 100); // Was value * 50 self.health = self.maxHealth; // Adjust speed based on chip value (tankier = slower) self.speed = 0.06 - (Math.log10(value) * 0.01); // Slightly faster base, but bigger chips are slower
User prompt
update as needed with: // In Card class calculateStats() var baseDamage = 20; // Reduced from 35 for gentler start var baseFireRate = 90; // Increased from 60 (slower firing) // Gentler level scaling self.damage = Math.floor(baseDamage * Math.pow(1.4, self.level - 1)); // From 1.8 self.fireRate = Math.max(30, Math.floor(baseFireRate / Math.pow(1.2, self.level - 1))); // From 1.3 // Cap card levels at 10 self.setLevel = function(newLevel) { if (self.cardData.suit === 'joker') { self.level = 1; levelText.visible = false; self.calculateStats(); return; } self.level = Math.min(10, newLevel); // Cap at level 10 levelText.setText('Lvl ' + self.level); self.calculateStats(); };
User prompt
Move to the next round if both bosses are defeated
User prompt
Reduce deal cost increase.
User prompt
Increase gold gained from enemies.
User prompt
Increase base damage of cards a little bit
User prompt
Improve the AI and make its turns more deliberate, performing actions one a time rather than all at once. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'if (originalCardPosition.area === 'player') {' Line Number: 1914
User prompt
Please fix the bug: 'TypeError: discardText.style is undefined' in or related to this line: 'discardText.style.fill = 0x999999;' Line Number: 1881
User prompt
add a discard area to the right of the players hand that the player can drag cards to for some gold refund ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
when a card is dragged, put it on the top layer
User prompt
increase gold amount received for defeating enemies
Code edit (1 edits merged)
Please save this source code
User prompt
update with: /**** * Wave Spawning System ****/ var WaveSystem = { playerSpawnTimer: 0, aiSpawnTimer: 0, waveNumber: 1, waveTimer: 0, waveDuration: 1800, // 30 seconds per wave (30 * 60 ticks) spawnInterval: 45, // Much faster: spawn every 0.75 seconds instead of 2 seconds bossSpawned: false, // Track if boss has been spawned this wave getChipValue: function(waveNumber) { // Wave 1: Only 1-chips if (waveNumber === 1) { return 1; } // Wave 2-3: Mostly 1s, rare 5s else if (waveNumber <= 3) { return Math.random() < 0.1 ? 5 : 1; // 10% chance of 5-chip } // Wave 4-6: More 5s mixed in else if (waveNumber <= 6) { return Math.random() < 0.25 ? 5 : 1; // 25% chance of 5-chip } // Wave 7-9: Equal mix of 1s and 5s else if (waveNumber <= 9) { return Math.random() < 0.5 ? 5 : 1; // 50% chance of 5-chip } // Wave 11-15: Introduce 10-chips else if (waveNumber <= 15) { var rand = Math.random(); if (rand < 0.1) return 10; else if (rand < 0.6) return 5; else return 1; } // Wave 16-19: More variety else if (waveNumber <= 19) { var rand = Math.random(); if (rand < 0.2) return 10; else if (rand < 0.7) return 5; else return 1; } // Wave 21+: Keep scaling gradually else if (!this.isBossWave(waveNumber)) { var rand = Math.random(); if (rand < 0.05) return 25; else if (rand < 0.3) return 10; else if (rand < 0.8) return 5; else return 1; } }, // Sometimes spawn multiple chips at once for more intensity getChipCount: function(waveNumber) { if (this.isBossWave(waveNumber)) { return 1; // Only one boss } // 20% chance to spawn 2-3 chips at once for more action var rand = Math.random(); if (rand < 0.15) { return 3; // Triple spawn } else if (rand < 0.35) { return 2; // Double spawn } else { return 1; // Single spawn } }, getBossChipValue: function(waveNumber) { // Single massive boss enemy if (waveNumber === 10) { return 250; // First boss } else if (waveNumber === 20) { return 500; // Second boss } else if (waveNumber === 30) { return 1000; // Third boss } else { // Later bosses scale dramatically var bossLevel = Math.floor(waveNumber / 10); return 250 * Math.pow(2, bossLevel - 1); } }, isBossWave: function(waveNumber) { return waveNumber % 10 === 0; }, spawnChip: function(isPlayerSide) { if (this.isBossWave(this.waveNumber)) { // Boss wave: spawn ONE big enemy at the start, then nothing if (!this.bossSpawned) { var bossValue = this.getBossChipValue(this.waveNumber); ChipSpawner.spawnChip(bossValue, isPlayerSide); this.bossSpawned = true; console.log("BOSS spawned with value:", bossValue); } // Don't spawn anything else during boss wave return; } // Normal wave spawning - sometimes multiple enemies var chipCount = this.getChipCount(this.waveNumber); for (var i = 0; i < chipCount; i++) { var chipValue = this.getChipValue(this.waveNumber); // Slight delay between multiple spawns so they don't all overlap if (i === 0) { ChipSpawner.spawnChip(chipValue, isPlayerSide); } else { LK.setTimeout(function(value, side) { return function() { ChipSpawner.spawnChip(value, side); }; }(chipValue, isPlayerSide), i * 10); // 10 tick delay between multiple spawns } } }, update: function() { this.waveTimer++; // Check if wave is complete if (this.waveTimer >= this.waveDuration) { this.waveTimer = 0; this.waveNumber++; this.playerSpawnTimer = 0; this.aiSpawnTimer = 0; this.bossSpawned = false; // Reset boss spawn flag var waveType = this.isBossWave(this.waveNumber) ? "BOSS WAVE" : "Wave"; console.log(waveType + " " + this.waveNumber + " starting!"); return; } // Spawn on player side this.playerSpawnTimer++; if (this.playerSpawnTimer >= this.spawnInterval) { this.playerSpawnTimer = 0; this.spawnChip(true); } // Spawn on AI side this.aiSpawnTimer++; if (this.aiSpawnTimer >= this.spawnInterval) { this.aiSpawnTimer = 0; this.spawnChip(false); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.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 = 10.4;
self.isSeekingLastPosition = false;
self.targetLastX = 0;
self.targetLastY = 0;
var currentGraphic = null;
var suitGraphics = {
'hearts': self.attachAsset('heartSuit', {
anchorX: 0.5,
anchorY: 0.5
}),
'diamonds': self.attachAsset('diamondSuit', {
anchorX: 0.5,
anchorY: 0.5
}),
'clubs': self.attachAsset('clubSuit', {
anchorX: 0.5,
anchorY: 0.5
}),
'spades': self.attachAsset('spadeSuit', {
anchorX: 0.5,
anchorY: 0.5
})
};
// Set scale and hide all suit graphics initially.
for (var suit in suitGraphics) {
var graphic = suitGraphics[suit];
graphic.scale.set(0.3); // Small bullets
graphic.visible = false;
}
// A generic bullet for jokers or other cases
var defaultBulletGraphic = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.activate = function (startX, startY, target, damage, suit) {
self.active = true;
self.visible = true;
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.isSeekingLastPosition = false;
if (self.target) {
self.targetLastX = self.target.x;
self.targetLastY = self.target.y;
}
if (currentGraphic) {
currentGraphic.visible = false;
}
if (suit && suitGraphics[suit]) {
currentGraphic = suitGraphics[suit];
} else {
currentGraphic = defaultBulletGraphic;
}
currentGraphic.visible = true;
};
self.update = function () {
if (!self.active) {
return;
}
// If target is destroyed, switch to seeking its last known position
if (self.target && !self.target.active) {
self.isSeekingLastPosition = true;
self.target = null;
}
// If seeking, move towards the last position without a tween
if (self.isSeekingLastPosition) {
var dx = self.targetLastX - self.x;
var dy = self.targetLastY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
// Arrived at destination, recycle bullet
self.isSeekingLastPosition = false;
if (currentGraphic) {
currentGraphic.visible = false;
}
PoolManager.returnBullet(self);
} else {
// Move towards destination
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
} else if (self.target) {
// Target is active and present
// Update last known position and move towards active 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 distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
// Hit target, deal damage and recycle
self.target.takeDamage(self.damage);
if (currentGraphic) {
currentGraphic.visible = false;
}
PoolManager.returnBullet(self);
} else {
// Move towards target
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
} else {
// No target and not seeking, probably an edge case. Recycle.
if (currentGraphic) {
currentGraphic.visible = false;
}
PoolManager.returnBullet(self);
}
};
return self;
});
/****
* Object Pool Manager
****/
/****
* Card Class
****/
var Card = Container.expand(function (cardData) {
var self = Container.call(this);
self.cardData = cardData;
self.level = 1;
self.isInPlay = false;
self.isPlayerCard = true; // Track which player owns this card
self.playSlotX = 0;
self.playSlotY = 0;
self.lastFired = 0;
self.fireRate = 60; // Base fire rate (ticks between shots)
self.damage = 35; // Increased base damage
self.range = 200;
self.handBonus = 1;
var cardGraphics = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
// Define getSuitSymbol method before using it
self.getSuitSymbol = function (suit) {
switch (suit) {
case 'hearts':
return '♥';
case 'diamonds':
return '♦';
case 'clubs':
return '♣';
case 'spades':
return '♠';
case 'joker':
return '🃏';
default:
return '?';
}
};
// Card value in top left corner
var valueText = new Text2(cardData.value, {
size: 45,
fill: CardSystem.suitColors[cardData.suit] || 0x000000,
weight: 800
});
valueText.anchor.set(0, 0);
valueText.x = -95; // Top left
valueText.y = -135;
self.addChild(valueText);
// Large suit symbol in center
var suitAssetId = null;
switch (cardData.suit) {
case 'hearts':
suitAssetId = 'heartSuit';
break;
case 'diamonds':
suitAssetId = 'diamondSuit';
break;
case 'clubs':
suitAssetId = 'clubSuit';
break;
case 'spades':
suitAssetId = 'spadeSuit';
break;
}
if (suitAssetId) {
var suitGraphics = self.attachAsset(suitAssetId, {
anchorX: 0.5,
anchorY: 0.5
});
suitGraphics.y = -15; // Slightly above center
// Scale to appropriate size
suitGraphics.scaleX = suitGraphics.scaleY = 0.8;
} else if (cardData.suit === 'joker') {
// For jokers, keep the text symbol
var suitText = new Text2('★', {
size: 120,
fill: CardSystem.suitColors[cardData.suit] || 0x000000,
weight: 800
});
suitText.anchor.set(0.5, 0.5);
suitText.y = -15; // Slightly above center
self.addChild(suitText);
}
// Level text at bottom
var levelText = new Text2('Lvl 1', {
size: 38,
fill: 0x000000,
weight: 800
});
levelText.anchor.set(0.5, 1);
levelText.y = 128; // Bottom of card
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 () {
// Stats based on level only, not card face value
var baseDamage = 35; // Increased from 25 for better damage output
var baseFireRate = 60;
// Level scaling - more dramatic improvements per level
self.damage = Math.floor(baseDamage * Math.pow(1.8, self.level - 1)); // Increased scaling
self.fireRate = Math.max(15, Math.floor(baseFireRate / Math.pow(1.3, self.level - 1)));
// Apply poker hand bonus
self.damage = Math.floor(self.damage * self.handBonus);
self.fireRate = Math.max(10, Math.floor(self.fireRate / self.handBonus));
};
self.setLevel = function (newLevel) {
if (self.cardData.suit === 'joker') {
self.level = 1;
levelText.visible = false;
self.calculateStats();
return;
}
self.level = newLevel;
levelText.setText('Lvl ' + self.level);
self.calculateStats();
// Visual feedback for higher levels
if (self.level > 1) {
// The glow effect was causing cards to become translucent.
// Level up is already indicated by animation and floating text.
}
};
self.canMergeWith = function (otherCard) {
if (!otherCard || otherCard === self) {
return false;
}
// If the card being dropped onto is a Joker, it cannot be leveled up.
if (otherCard.cardData.suit === 'joker') {
return false;
}
// If the card being dragged is a Joker, it can merge with any non-Joker card.
if (self.cardData.suit === 'joker') {
return true;
}
// Must be same level AND (same suit OR same value)
var sameLevel = self.level === otherCard.level;
var sameSuit = self.cardData.suit === otherCard.cardData.suit;
var sameValue = self.cardData.value === otherCard.cardData.value;
return sameLevel && (sameSuit || sameValue);
};
self.mergeWith = function (otherCard) {
if (!self.canMergeWith(otherCard)) {
return null;
}
// When merging, the new card levels up. The new level is one higher than the card on the board.
var newLevel = otherCard.level + 1;
// Special case: If the card being dragged is a Joker, the target card just increases its level
if (self.cardData.suit === 'joker') {
var mergedCard = new Card(otherCard.cardData);
mergedCard.setLevel(newLevel);
return mergedCard;
}
// The new card is of a random type, regardless of what was merged.
var randomSuit = CardSystem.suits[Math.floor(Math.random() * CardSystem.suits.length)];
var randomValue = CardSystem.values[Math.floor(Math.random() * CardSystem.values.length)];
var newCardData = {
suit: randomSuit,
value: randomValue,
id: randomSuit + '_' + randomValue
};
var mergedCard = new Card(newCardData);
mergedCard.setLevel(newLevel);
return mergedCard;
};
self.findTarget = function () {
// Player cards target enemies attacking the player (activePlayerChips)
// AI cards target enemies attacking the AI (activeAIChips)
var targets = self.isPlayerCard ? activePlayerChips : activeAIChips;
for (var i = 0; i < targets.length; i++) {
if (targets[i].active) {
return targets[i];
}
}
return null;
};
self.fire = function () {
var target = self.findTarget();
if (!target) {
return;
}
var bullet = PoolManager.getBullet();
if (bullet) {
bullet.activate(self.x, self.y, target, self.damage, self.cardData.suit);
gameLayer.addChild(bullet);
activeBullets.push(bullet);
self.lastFired = LK.ticks;
// Visual feedback for firing
tween.stop(self, {
scaleX: true,
scaleY: true
});
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.quadOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.elasticOut
});
}
});
}
};
self.update = function () {
if (!self.isInPlay) {
return;
}
if (LK.ticks - self.lastFired >= self.fireRate) {
self.fire();
}
};
// Initialize with level 1
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; // Increased base health
self.maxHealth = 100;
self.value = 1;
self.speed = 0.05;
self.pathProgress = 0;
self.isPlayerSide = true;
var chipGraphicsAssets = {
1: self.attachAsset('yellowChip', {
anchorX: 0.5,
anchorY: 0.5
}),
5: self.attachAsset('redChip', {
anchorX: 0.5,
anchorY: 0.5
}),
10: self.attachAsset('greenChip', {
anchorX: 0.5,
anchorY: 0.5
}),
25: self.attachAsset('blueChip', {
anchorX: 0.5,
anchorY: 0.5
}),
100: self.attachAsset('purpleChip', {
anchorX: 0.5,
anchorY: 0.5
})
};
var chipGraphics = null; // This will hold the current visible chip
for (var val in chipGraphicsAssets) {
chipGraphicsAssets[val].visible = false;
}
var healthText = new Text2('', {
size: 60,
fill: 0x000000,
weight: 800
});
healthText.anchor.set(0.5, 0.5);
self.addChild(healthText);
self.activate = function (value, isPlayerSide) {
self.active = true;
self.visible = true;
self.value = value;
self.isPlayerSide = isPlayerSide;
// Health scales with chip value - more valuable chips are tankier
self.maxHealth = value * 50; // Much higher base health
self.health = self.maxHealth;
self.pathProgress = 0;
// Set speed based on chip value - 1 value chips move slower
if (value === 1) {
self.speed = 0.03; // Slower speed for 1 value chips
} else {
self.speed = 0.05; // Normal speed for other chips
}
self.setChipAppearance();
var startPos = PathSystem.getPathStart(isPlayerSide);
self.x = startPos.x;
self.y = startPos.y;
};
self.setChipAppearance = function () {
if (chipGraphics) {
chipGraphics.visible = false;
}
chipGraphics = chipGraphicsAssets[self.value] || chipGraphicsAssets[1];
if (chipGraphics) {
chipGraphics.visible = true;
}
healthText.setText(Math.round(self.health).toString());
};
self.takeDamage = function (damage) {
self.health -= damage;
healthText.setText(Math.round(Math.max(0, self.health)).toString());
// Flash red when taking damage
if (chipGraphics) {
chipGraphics.tint = 0xff0000; // Flash red
LK.setTimeout(function () {
if (chipGraphics) {
chipGraphics.tint = 0xffffff; // Reset tint
}
}, 100);
}
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
var goldEarned = Math.ceil(self.value * 2.25);
if (self.isPlayerSide) {
gameState.playerGold += goldEarned;
} else {
gameState.aiGold += goldEarned;
}
PoolManager.returnChip(self);
};
self.update = function () {
if (!self.active) {
return;
}
self.pathProgress += self.speed;
var pathPos = PathSystem.getPositionAlongPath(self.pathProgress, self.isPlayerSide);
if (pathPos.completed) {
if (self.isPlayerSide) {
gameState.playerLives--;
} else {
gameState.aiLives--;
}
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
****/
/****
* Poker Tower Defense - Complete Refactor
****/
/****
* Game Constants
****/
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 _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 _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;
// AI area positioning (top)
var AI_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2;
var AI_AREA_Y = 150;
// Player area positioning (middle, with plenty of room below)
var PLAYER_AREA_X = (SCREEN_WIDTH - PLAY_AREA_COLS * SLOT_WIDTH) / 2;
var PLAYER_AREA_Y = SCREEN_HEIGHT - 1300; // Much higher up
// Player deal area (hand slots) - below play area
var PLAYER_DEAL_AREA_Y = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + 40;
/****
* Card System
****/
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 deck = [];
var _iterator = _createForOfIteratorHelper(this.suits),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var suit = _step.value;
var _iterator2 = _createForOfIteratorHelper(this.values),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var value = _step2.value;
deck.push({
suit: suit,
value: value,
id: suit + '_' + value
});
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
}
// Add jokers
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
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 temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
return deck;
},
getCardValue: function getCardValue(card) {
if (card.suit === 'joker') {
return 14;
} // Jokers are highest
if (card.value === 'A') {
return 14;
} // Aces high
if (card.value === 'K') {
return 13;
}
if (card.value === 'Q') {
return 12;
}
if (card.value === 'J') {
return 11;
}
return parseInt(card.value);
},
evaluatePokerHand: function evaluatePokerHand(cards) {
var _this = this;
if (!cards || cards.length === 0) {
return {
type: 'none',
strength: 0,
multiplier: 1
};
}
// Sort cards by value for easier analysis
var sortedCards = cards.slice().sort(function (a, b) {
return _this.getCardValue(b) - _this.getCardValue(a);
});
var values = sortedCards.map(function (card) {
return _this.getCardValue(card);
});
var suits = sortedCards.map(function (card) {
return card.suit;
});
// Count values and suits
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;
});
// Only check for 5-card hands if we have 5 cards
var isFlush = false;
var isStraight = false;
if (cards.length === 5) {
isFlush = Object.keys(suitCounts).length === 1;
isStraight = this.checkStraight(values);
}
// Royal Flush
if (isFlush && isStraight && values[0] === 14 && values[4] === 10) {
return {
type: 'royal_flush',
strength: 10,
multiplier: 50
};
}
// Straight Flush
if (isFlush && isStraight) {
return {
type: 'straight_flush',
strength: 9,
multiplier: 25
};
}
// Four of a Kind
if (counts[0] === 4) {
return {
type: 'four_of_a_kind',
strength: 8,
multiplier: 15
};
}
// Full House
if (counts[0] === 3 && counts[1] === 2) {
return {
type: 'full_house',
strength: 7,
multiplier: 8
};
}
// Flush
if (isFlush) {
return {
type: 'flush',
strength: 6,
multiplier: 5
};
}
// Straight
if (isStraight) {
return {
type: 'straight',
strength: 5,
multiplier: 4
};
}
// Three of a Kind
if (counts[0] === 3) {
return {
type: 'three_of_a_kind',
strength: 4,
multiplier: 3
};
}
// Two Pair
if (counts[0] === 2 && counts[1] === 2) {
return {
type: 'two_pair',
strength: 3,
multiplier: 2.5
};
}
// One Pair
if (counts[0] === 2) {
return {
type: 'one_pair',
strength: 2,
multiplier: 1.5
};
}
// High Card
return {
type: 'high_card',
strength: 1,
multiplier: 1
};
},
checkStraight: function checkStraight(values) {
if (values.length !== 5) {
return false;
}
// Check for ace-low straight (A, 2, 3, 4, 5)
if (values[0] === 14 && values[1] === 5 && values[2] === 4 && values[3] === 3 && values[4] === 2) {
return true;
}
// Check normal straight
for (var i = 0; i < 4; i++) {
if (values[i] - values[i + 1] !== 1) {
return false;
}
}
return true;
}
};
/****
* Object Pool Manager
****/
var PoolManager = {
chipPool: [],
bulletPool: [],
cardPool: [],
CHIP_POOL_SIZE: 50,
BULLET_POOL_SIZE: 100,
CARD_POOL_SIZE: 60,
init: function init() {
// Initialize pools
for (var i = 0; i < this.CHIP_POOL_SIZE; i++) {
var chip = new PokerChip();
chip.active = false;
chip.visible = false;
this.chipPool.push(chip);
}
for (var i = 0; i < this.BULLET_POOL_SIZE; i++) {
var bullet = new Bullet();
bullet.active = false;
bullet.visible = false;
this.bulletPool.push(bullet);
}
},
getChip: function getChip() {
for (var i = 0; i < this.chipPool.length; i++) {
if (!this.chipPool[i].active) {
return this.chipPool[i];
}
}
return null;
},
getBullet: function getBullet() {
for (var i = 0; i < this.bulletPool.length; i++) {
if (!this.bulletPool[i].active) {
return this.bulletPool[i];
}
}
return null;
},
returnChip: function returnChip(chip) {
chip.active = false;
chip.visible = false;
// Remove from active arrays
var playerIndex = activePlayerChips.indexOf(chip);
if (playerIndex !== -1) {
activePlayerChips.splice(playerIndex, 1);
}
var aiIndex = activeAIChips.indexOf(chip);
if (aiIndex !== -1) {
activeAIChips.splice(aiIndex, 1);
}
gameLayer.removeChild(chip);
},
returnBullet: function returnBullet(bullet) {
bullet.active = false;
bullet.visible = false;
var index = activeBullets.indexOf(bullet);
if (index !== -1) {
activeBullets.splice(index, 1);
}
gameLayer.removeChild(bullet);
}
};
/****
* Path System
****/
var PathSystem = {
playerPath: [],
aiPath: [],
init: function init() {
// Create player path - rectangular loop around the play area
var padding = 80; // Distance from play area
var leftX = PLAYER_AREA_X - padding;
var rightX = PLAYER_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding;
var topY = PLAYER_AREA_Y - padding;
var bottomY = PLAYER_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + padding;
this.playerPath = [
// Start at bottom left
{
x: leftX,
y: bottomY
},
// Go up the left side
{
x: leftX,
y: topY
},
// Go across the top
{
x: rightX,
y: topY
},
// Go down the right side
{
x: rightX,
y: bottomY
}];
// Create AI path - UPSIDE DOWN mirror of player path
var aiLeftX = AI_AREA_X - padding;
var aiRightX = AI_AREA_X + PLAY_AREA_COLS * SLOT_WIDTH + padding;
var aiTopY = AI_AREA_Y - padding;
var aiBottomY = AI_AREA_Y + PLAY_AREA_ROWS * SLOT_HEIGHT + padding;
this.aiPath = [
// Start at TOP left (opposite of player)
{
x: aiLeftX,
y: aiTopY
},
// Go DOWN the left side (opposite of player)
{
x: aiLeftX,
y: aiBottomY
},
// Go across the BOTTOM (opposite of player)
{
x: aiRightX,
y: aiBottomY
},
// Go UP the right side (opposite of player)
{
x: aiRightX,
y: aiTopY
}];
},
getPathStart: function getPathStart(isPlayerSide) {
return isPlayerSide ? this.playerPath[0] : this.aiPath[0];
},
getPositionAlongPath: function getPositionAlongPath(progress, isPlayerSide) {
var path = isPlayerSide ? this.playerPath : this.aiPath;
var pathLength = this.calculatePathLength(path);
var targetDistance = progress / 100 * pathLength;
if (targetDistance >= pathLength) {
return {
x: path[path.length - 1].x,
y: path[path.length - 1].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;
}
return {
x: path[path.length - 1].x,
y: path[path.length - 1].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;
}
chip.activate(value, isPlayerSide);
gameLayer.addChild(chip);
if (isPlayerSide) {
activePlayerChips.push(chip);
} else {
activeAIChips.push(chip);
}
}
};
/****
* Wave Spawning System
****/
var WaveSystem = {
playerSpawnTimer: 0,
aiSpawnTimer: 0,
waveNumber: 1,
waveTimer: 0,
waveDuration: 1800,
// 30 seconds per wave (30 * 60 ticks)
spawnInterval: 60,
// Much faster: spawn every 0.75 seconds instead of 2 seconds
bossSpawned: false,
// Track if boss has been spawned this wave
getChipValue: function getChipValue(waveNumber) {
// Wave 1: Only 1-chips
if (waveNumber === 1) {
return 1;
}
// Wave 2-3: Mostly 1s, rare 5s
else if (waveNumber <= 3) {
return Math.random() < 0.1 ? 5 : 1; // 10% chance of 5-chip
}
// Wave 4-6: More 5s mixed in
else if (waveNumber <= 6) {
return Math.random() < 0.25 ? 5 : 1; // 25% chance of 5-chip
}
// Wave 7-9: Equal mix of 1s and 5s
else if (waveNumber <= 9) {
return Math.random() < 0.5 ? 5 : 1; // 50% chance of 5-chip
}
// Wave 11-15: Introduce 10-chips
else if (waveNumber <= 15) {
var rand = Math.random();
if (rand < 0.1) {
return 10;
} else if (rand < 0.6) {
return 5;
} else {
return 1;
}
}
// Wave 16-19: More variety
else if (waveNumber <= 19) {
var rand = Math.random();
if (rand < 0.2) {
return 10;
} else if (rand < 0.7) {
return 5;
} else {
return 1;
}
}
// Wave 21+: Keep scaling gradually
else if (!this.isBossWave(waveNumber)) {
var rand = Math.random();
if (rand < 0.05) {
return 25;
} else if (rand < 0.3) {
return 10;
} else if (rand < 0.8) {
return 5;
} else {
return 1;
}
}
},
// Sometimes spawn multiple chips at once for more intensity
getChipCount: function getChipCount(waveNumber) {
if (this.isBossWave(waveNumber)) {
return 1; // Only one boss
}
// 20% chance to spawn 2-3 chips at once for more action
var rand = Math.random();
if (rand < 0.15) {
return 3; // Triple spawn
} else if (rand < 0.35) {
return 2; // Double spawn
} else {
return 1; // Single spawn
}
},
getBossChipValue: function getBossChipValue(waveNumber) {
// Single massive boss enemy
if (waveNumber === 10) {
return 250; // First boss
} else if (waveNumber === 20) {
return 500; // Second boss
} else if (waveNumber === 30) {
return 1000; // Third boss
} else {
// Later bosses scale dramatically
var bossLevel = Math.floor(waveNumber / 10);
return 250 * Math.pow(2, bossLevel - 1);
}
},
isBossWave: function isBossWave(waveNumber) {
return waveNumber % 10 === 0;
},
spawnChip: function spawnChip(isPlayerSide) {
if (this.isBossWave(this.waveNumber)) {
// Boss wave: spawn ONE big enemy at the start, then nothing
if (!this.bossSpawned) {
var bossValue = this.getBossChipValue(this.waveNumber);
ChipSpawner.spawnChip(bossValue, isPlayerSide);
this.bossSpawned = true;
console.log("BOSS spawned with value:", bossValue);
}
// Don't spawn anything else during boss wave
return;
}
// Normal wave spawning - sometimes multiple enemies
var chipCount = this.getChipCount(this.waveNumber);
for (var i = 0; i < chipCount; i++) {
var chipValue = this.getChipValue(this.waveNumber);
// Slight delay between multiple spawns so they don't all overlap
if (i === 0) {
ChipSpawner.spawnChip(chipValue, isPlayerSide);
} else {
LK.setTimeout(function (value, side) {
return function () {
ChipSpawner.spawnChip(value, side);
};
}(chipValue, isPlayerSide), i * 10); // 10 tick delay between multiple spawns
}
}
},
playerBossDefeated: false,
aiBossDefeated: false,
update: function update() {
this.waveTimer++;
// Check if wave is complete
if (this.waveTimer >= this.waveDuration) {
// Check if both bosses were defeated in a boss wave
if (this.isBossWave(this.waveNumber) && this.playerBossDefeated && this.aiBossDefeated) {
console.log("Both bosses defeated! Moving to next round!");
createFloatingText('ROUND COMPLETE!', SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0x00ff00);
// Reset boss defeat flags
this.playerBossDefeated = false;
this.aiBossDefeated = false;
}
this.waveTimer = 0;
this.waveNumber++;
this.playerSpawnTimer = 0;
this.aiSpawnTimer = 0;
this.bossSpawned = false; // Reset boss spawn flag
var waveType = this.isBossWave(this.waveNumber) ? "BOSS WAVE" : "Wave";
console.log(waveType + " " + this.waveNumber + " starting!");
return;
}
// During boss wave, check if no enemies remain (boss defeated)
if (this.isBossWave(this.waveNumber) && this.bossSpawned) {
// Check if player side boss is defeated
if (!this.playerBossDefeated && activePlayerChips.length === 0) {
this.playerBossDefeated = true;
console.log("Player defeated the boss!");
createFloatingText('BOSS DEFEATED!', SCREEN_WIDTH / 2, PLAYER_AREA_Y + SLOT_HEIGHT, 0xffd700);
}
// Check if AI side boss is defeated
if (!this.aiBossDefeated && activeAIChips.length === 0) {
this.aiBossDefeated = true;
console.log("AI defeated the boss!");
createFloatingText('AI BOSS DEFEATED!', SCREEN_WIDTH / 2, AI_AREA_Y + SLOT_HEIGHT, 0xffd700);
}
// If both bosses defeated, immediately end the wave
if (this.playerBossDefeated && this.aiBossDefeated) {
this.waveTimer = this.waveDuration - 1; // Set to end on next tick
}
}
// Spawn on player side
this.playerSpawnTimer++;
if (this.playerSpawnTimer >= this.spawnInterval) {
this.playerSpawnTimer = 0;
this.spawnChip(true);
}
// Spawn on AI side
this.aiSpawnTimer++;
if (this.aiSpawnTimer >= this.spawnInterval) {
this.aiSpawnTimer = 0;
this.spawnChip(false);
}
}
};
/****
* Game State
****/
var gameState = {
playerGold: 200,
// Increased starting gold
aiGold: 200,
playerLives: 3,
aiLives: 3,
isPlayerTurn: true,
dealCost: 25,
// Initial deal cost
dealCount: 0,
playerDeck: [],
playerHand: [],
playerPlayArea: [],
aiDeck: [],
aiPlayArea: []
};
/****
* Game Variables
****/
var activePlayerChips = [];
var activeAIChips = [];
var activeBullets = [];
var playerHandNameTexts = [];
var selectedCard = null;
var isDragging = false;
var originalCardPosition = null;
var gameLayer = new Container();
var uiLayer = new Container();
game.addChild(gameLayer);
game.addChild(uiLayer);
/****
* UI Elements
****/
var playerGoldText = new Text2('Gold: 200', {
size: 50,
fill: 0xffd700,
weight: 800
});
playerGoldText.x = 50;
playerGoldText.y = SCREEN_HEIGHT - 120;
uiLayer.addChild(playerGoldText);
var playerLivesText = new Text2('Lives: 3', {
size: 50,
fill: 0xff0000,
weight: 800
});
playerLivesText.x = 50;
playerLivesText.y = SCREEN_HEIGHT - 180;
uiLayer.addChild(playerLivesText);
// Add AI stats too for clarity
var aiGoldText = new Text2('AI Gold: 200', {
size: 40,
fill: 0xffd700,
weight: 800
});
aiGoldText.x = 50;
aiGoldText.y = 50;
uiLayer.addChild(aiGoldText);
var aiLivesText = new Text2('AI Lives: 3', {
size: 40,
fill: 0xff0000,
weight: 800
});
aiLivesText.x = 50;
aiLivesText.y = 100;
uiLayer.addChild(aiLivesText);
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xffffff,
weight: 800
});
waveText.x = SCREEN_WIDTH - 200;
waveText.y = 50;
uiLayer.addChild(waveText);
var discardAreaContainer = new Container();
var discardAreaGraphic = discardAreaContainer.attachAsset('dealButton', {
anchorX: 0.5,
anchorY: 0.5
});
var discardText = new Text2('Deal (25)', {
size: 40,
fill: 0xffffff,
weight: 800
});
discardText.anchor.set(0.5, 0.5);
discardAreaContainer.addChild(discardText);
// Position it right of the hand
var handWidthForDiscard = 5 * DEAL_SLOT_WIDTH + 4 * 30;
var handStartXForDiscard = (SCREEN_WIDTH - handWidthForDiscard) / 2;
var discardX = handStartXForDiscard + handWidthForDiscard + 30 + DEAL_SLOT_WIDTH / 2;
// Make sure it doesn't go off screen
if (discardX + DEAL_SLOT_WIDTH / 2 > SCREEN_WIDTH) {
discardX = SCREEN_WIDTH - DEAL_SLOT_WIDTH / 2 - 20;
}
discardAreaContainer.x = discardX;
discardAreaContainer.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
uiLayer.addChild(discardAreaContainer);
discardAreaContainer.down = function () {
if (!isDragging && gameState.playerGold >= gameState.dealCost) {
dealNewHand();
}
};
/****
* Game Functions
****/
function initializeGame() {
PoolManager.init();
PathSystem.init();
// Initialize play areas
gameState.playerPlayArea = [];
gameState.aiPlayArea = [];
playerHandNameTexts = [null, null];
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
gameState.playerPlayArea[row] = [];
gameState.aiPlayArea[row] = [];
for (var col = 0; col < PLAY_AREA_COLS; col++) {
gameState.playerPlayArea[row][col] = null;
gameState.aiPlayArea[row][col] = null;
}
}
// Create initial decks
gameState.playerDeck = CardSystem.createDeck();
gameState.aiDeck = CardSystem.createDeck();
// Draw grid lines
drawPlayAreas();
drawPaths();
// Initialize player's hand with empty slots
gameState.playerHand = [null, null, null, null, null];
// Start the wave system - first waves begin immediately
LK.setTimeout(function () {
ChipSpawner.spawnChip(1, true); // First enemy on player side
ChipSpawner.spawnChip(1, false); // First enemy on AI side
}, 1000);
}
function dealNewHand() {
if (gameState.playerGold < gameState.dealCost) {
return;
}
// Find an empty slot in the player's hand
var emptySlotIndex = -1;
for (var i = 0; i < gameState.playerHand.length; i++) {
if (!gameState.playerHand[i]) {
emptySlotIndex = i;
break;
}
}
// If hand is full, do nothing
if (emptySlotIndex === -1) {
return;
}
gameState.playerGold -= gameState.dealCost;
gameState.dealCount++;
gameState.dealCost = Math.floor(25 * Math.pow(1.05, gameState.dealCount)); // Lower base cost for single card
if (gameState.playerDeck.length === 0) {
gameState.playerDeck = CardSystem.createDeck();
}
var cardData = gameState.playerDeck.pop();
var card = new Card(cardData);
var i = emptySlotIndex;
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
var slotX = handStartX + i * DEAL_SLOT_WIDTH + i * 30 + DEAL_SLOT_WIDTH / 2;
var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
card.activate(slotX, slotY, false, true); // true = is player card
uiLayer.addChild(card);
gameState.playerHand[i] = card;
updateUI();
}
function updateUI() {
playerGoldText.setText('Gold: ' + gameState.playerGold);
playerLivesText.setText('Lives: ' + gameState.playerLives);
aiGoldText.setText('AI Gold: ' + gameState.aiGold);
aiLivesText.setText('AI Lives: ' + gameState.aiLives);
// Update combined deal/discard button text and appearance when not dragging
if (!isDragging) {
discardText.setText('Deal (' + gameState.dealCost + ')');
discardText.fill = 0xffffff;
// Update button color based on affordability
if (gameState.playerGold >= gameState.dealCost) {
discardAreaGraphic.tint = 0x00aa00;
} else {
discardAreaGraphic.tint = 0x666666;
}
}
waveText.setText('Wave: ' + WaveSystem.waveNumber);
}
function drawPlayAreas() {
// Draw player play area slots
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);
}
}
// Draw AI play area slots
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var slot = new Container();
var slotGraphics = slot.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
slotGraphics.alpha = 0.5;
slotGraphics.tint = 0xff8888; // Red tint for AI area
slot.x = AI_AREA_X + col * SLOT_WIDTH + SLOT_WIDTH / 2;
slot.y = AI_AREA_Y + row * SLOT_HEIGHT + SLOT_HEIGHT / 2;
gameLayer.addChild(slot);
}
}
// Draw player deal area slots (hand)
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.2;
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
dealSlot.x = handStartX + i * DEAL_SLOT_WIDTH + i * 30 + DEAL_SLOT_WIDTH / 2;
dealSlot.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
gameLayer.addChild(dealSlot);
}
}
function drawPaths() {
// Draw player path
for (var i = 0; i < PathSystem.playerPath.length - 1; i++) {
var start = PathSystem.playerPath[i];
var end = PathSystem.playerPath[i + 1];
drawPathSegment(start, end, 0x666666);
}
// Draw AI path
for (var i = 0; i < PathSystem.aiPath.length - 1; i++) {
var start = PathSystem.aiPath[i];
var end = PathSystem.aiPath[i + 1];
drawPathSegment(start, end, 0x664444);
}
}
function drawPathSegment(start, end, color) {
var dx = end.x - start.x;
var dy = end.y - start.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.floor(distance / 20);
for (var i = 0; i <= segments; i++) {
var progress = i / segments;
var x = start.x + dx * progress;
var y = start.y + dy * progress;
var pathDot = new Container();
var dotGraphics = pathDot.attachAsset('pathSegment', {
anchorX: 0.5,
anchorY: 0.5
});
dotGraphics.width = dotGraphics.height = 8;
dotGraphics.tint = color;
dotGraphics.alpha = 0.6;
pathDot.x = x;
pathDot.y = y;
gameLayer.addChild(pathDot);
}
}
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) {
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
for (var i = 0; i < 5; i++) {
var slotXStart = handStartX + i * (DEAL_SLOT_WIDTH + 30);
var slotXEnd = slotXStart + DEAL_SLOT_WIDTH;
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].cardData);
}
}
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'
});
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() {
// Apply bonuses to player cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
var handEval = evaluateRowHand(row, true);
updateHandNameDisplay(row, handEval);
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.playerPlayArea[row][col];
if (card) {
card.handBonus = handEval.multiplier;
card.calculateStats();
}
}
}
// Apply bonuses to AI cards
AISystem.applyAIHandBonuses();
}
/****
* AI System
****/
var AISystem = {
thinkTimer: 0,
thinkDelay: 60,
// Think every second (60 ticks)
update: function update() {
this.thinkTimer++;
if (this.thinkTimer >= this.thinkDelay) {
this.thinkTimer = 0;
this.makeMove();
}
},
shouldDeal: function shouldDeal() {
// Deal if we can afford it and have empty slots
if (gameState.aiGold < gameState.dealCost) {
return false;
}
var emptySlots = this.countEmptySlots();
// Deal if there are any empty slots at all
return emptySlots > 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;
},
countLowLevelCards: function countLowLevelCards() {
var count = 0;
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.aiPlayArea[row][col];
if (card && card.level <= 2) {
count++;
}
}
}
return count;
},
makeMove: function makeMove() {
// Priority order: Merge > Place > Optimize > Deal. Only one action per cycle.
if (this.tryMergeCards()) {
// Successfully merged, wait for next think cycle
return;
}
if (this.tryPlaceCards()) {
// Successfully placed cards
return;
}
if (this.optimizeCardPositions()) {
// Successfully optimized, wait for next think cycle
return;
}
// As a last resort, try to deal a new card if possible
if (this.shouldDeal()) {
this.dealAIHand();
}
},
tryMergeCards: function tryMergeCards() {
// Look for mergeable cards
for (var row1 = 0; row1 < PLAY_AREA_ROWS; row1++) {
for (var col1 = 0; col1 < PLAY_AREA_COLS; col1++) {
var card1 = gameState.aiPlayArea[row1][col1];
if (!card1) {
continue;
}
// Look for a card to merge with
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) {
// Remove old cards
gameLayer.removeChild(card1);
gameLayer.removeChild(card2);
gameState.aiPlayArea[row1][col1] = null;
gameState.aiPlayArea[row2][col2] = null;
// Place merged card in the first position
var pos = getSlotPosition(row1, col1, false);
mergedCard.activate(pos.x, pos.y, true, false);
gameLayer.addChild(mergedCard);
gameState.aiPlayArea[row1][col1] = mergedCard;
// Merge animation
mergedCard.alpha = 0;
mergedCard.scaleX = mergedCard.scaleY = 1.5;
tween(mergedCard, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
this.applyAIHandBonuses();
}
},
tryPlaceCards: function tryPlaceCards() {
// AI doesn't have a "hand" like player, it deals directly to board
// This function is for future expansion
return false;
},
optimizeCardPositions: function optimizeCardPositions() {
// Move stronger cards to better positions (like completing poker hands)
// For now, just try to complete rows for hand bonuses
return this.tryCompletePokerHands();
},
tryCompletePokerHands: function tryCompletePokerHands() {
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
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 row is almost complete, try to fill it strategically
if (rowCards.length >= 3 && emptyPositions.length > 0) {
// Look for cards in other rows that might complete a hand
if (this.tryMoveCardToCompleteHand(row, rowCards, emptyPositions[0])) {
return true; // A move was made, so we are done for this 'think' cycle
}
}
}
return false; // No move was made
},
tryMoveCardToCompleteHand: function tryMoveCardToCompleteHand(targetRow, existingCards, targetCol) {
// Look for cards in other positions that might help complete a hand
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
if (row === targetRow) {
continue;
}
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.aiPlayArea[row][col];
if (!card) {
continue;
}
// Simple heuristic: move cards of same suit or sequential values
var shouldMove = this.cardHelpsHand(card, existingCards);
if (shouldMove && Math.random() < 0.3) {
// 30% chance to move
// Move the card
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) {
// Simple heuristic to see if a card might help complete a poker hand
var suits = {};
var values = {};
existingCards.forEach(function (cardInfo) {
var c = cardInfo.card.cardData;
suits[c.suit] = (suits[c.suit] || 0) + 1;
values[c.value] = (values[c.value] || 0) + 1;
});
// Check if card matches existing suits or values
var cardSuit = card.cardData.suit;
var cardValue = card.cardData.value;
return suits[cardSuit] >= 2 || values[cardValue] >= 1;
},
dealAIHand: function dealAIHand() {
if (gameState.aiGold < gameState.dealCost) {
return;
}
gameState.aiGold -= gameState.dealCost;
// Deal one card to an empty slot
if (gameState.aiDeck.length === 0) {
gameState.aiDeck = CardSystem.createDeck();
}
var cardData = gameState.aiDeck.pop();
var card = new Card(cardData);
// Find best empty slot (prefer completing rows)
var bestSlot = this.findBestEmptySlot();
if (bestSlot) {
var pos = getSlotPosition(bestSlot.row, bestSlot.col, false);
card.activate(pos.x, pos.y, true, false);
gameLayer.addChild(card);
gameState.aiPlayArea[bestSlot.row][bestSlot.col] = card;
// Deal animation
card.alpha = 0;
card.scaleX = card.scaleY = 0.5;
tween(card, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
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;
}
// Sort by score (higher is better)
emptySlots.sort(function (a, b) {
return b.score - a.score;
});
return emptySlots[0];
},
evaluateSlotScore: function evaluateSlotScore(row, col) {
var score = 0;
var cardsInRow = 0;
// Count cards in this row
for (var c = 0; c < PLAY_AREA_COLS; c++) {
if (gameState.aiPlayArea[row][c]) {
cardsInRow++;
}
}
// Prefer completing rows
score += cardsInRow * 10;
// Slight preference for middle positions
score += (2 - Math.abs(col - 2)) * 2;
return score;
},
applyAIHandBonuses: function applyAIHandBonuses() {
// Apply poker hand bonuses to AI cards
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
var handEval = evaluateRowHand(row, false);
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.aiPlayArea[row][col];
if (card) {
card.handBonus = handEval.multiplier;
card.calculateStats();
}
}
}
}
};
/****
* Input Handling
****/
game.down = function (x, y, obj) {
// Check if clicking on a card in hand
for (var i = 0; i < gameState.playerHand.length; i++) {
var card = gameState.playerHand[i];
if (card && Math.abs(x - card.x) < DEAL_SLOT_WIDTH / 2 && Math.abs(y - card.y) < DEAL_SLOT_HEIGHT / 2) {
selectedCard = card;
isDragging = true;
originalCardPosition = {
area: 'hand',
index: i
};
uiLayer.addChild(selectedCard);
return;
}
}
// Check if clicking on a card in play area
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
var card = gameState.playerPlayArea[row][col];
if (card && Math.abs(x - card.x) < SLOT_WIDTH / 2 && Math.abs(y - card.y) < SLOT_HEIGHT / 2) {
selectedCard = card;
isDragging = true;
originalCardPosition = {
area: 'player',
row: row,
col: col
};
// Remove from current position
gameState.playerPlayArea[row][col] = null;
gameLayer.addChild(selectedCard);
return;
}
}
}
};
game.move = function (x, y, obj) {
if (isDragging && selectedCard) {
selectedCard.x = x;
selectedCard.y = y;
// Switch to discard mode when dragging
discardAreaGraphic.tint = 0x440000; // Dark red for discard
// Highlight discard area on hover
var isOverDiscard = discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2;
if (isOverDiscard) {
discardAreaGraphic.alpha = 1.0;
discardText.setText('Sell Card');
discardText.fill = 0xffd700; // Gold color
} else {
discardAreaGraphic.alpha = 0.7;
discardText.setText('Discard');
discardText.fill = 0x999999;
}
} else {
// When not dragging, show as deal button
updateUI();
}
};
game.up = function (x, y, obj) {
if (isDragging && selectedCard) {
isDragging = false;
// Reset to deal button mode
updateUI();
var targetSlot = getSlotFromPosition(x, y);
if (targetSlot) {
// Handle dropping card on discard area
if (targetSlot.area === 'discard') {
var goldRefund = 5 + selectedCard.level * 2;
gameState.playerGold += goldRefund;
// Remove from original position data
if (originalCardPosition.area === 'hand') {
gameState.playerHand[originalCardPosition.index] = null;
} else if (originalCardPosition.area === 'player') {
// This is already null from game.down, so this is just for safety.
gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = null;
}
// Remove card graphic from scene and memory
if (selectedCard.parent) {
selectedCard.parent.removeChild(selectedCard);
}
createFloatingText('+' + goldRefund + ' Gold', selectedCard.x, selectedCard.y, 0xffd700);
selectedCard = null;
originalCardPosition = null;
updateUI();
// Recalculate bonuses if a card was removed from play area
if (originalCardPosition && originalCardPosition.area === 'player') {
applyHandBonuses();
}
return; // Exit early
}
if (targetSlot.area === 'player') {
var existingCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col];
if (existingCard && selectedCard.canMergeWith(existingCard)) {
// Merge in play area
var mergedCard = selectedCard.mergeWith(existingCard);
if (mergedCard) {
gameLayer.removeChild(existingCard);
var handIndex = gameState.playerHand.indexOf(selectedCard);
if (handIndex !== -1) {
uiLayer.removeChild(selectedCard);
gameState.playerHand[handIndex] = null;
} else {
gameLayer.removeChild(selectedCard);
}
var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
mergedCard.activate(pos.x, pos.y, true);
gameLayer.addChild(mergedCard);
gameState.playerPlayArea[targetSlot.row][targetSlot.col] = mergedCard;
mergedCard.alpha = 0;
mergedCard.scaleX = mergedCard.scaleY = 2;
tween(mergedCard, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
}
} else if (!existingCard) {
// Place in empty slot
var pos = getSlotPosition(targetSlot.row, targetSlot.col, true);
selectedCard.activate(pos.x, pos.y, true);
var handIndex = gameState.playerHand.indexOf(selectedCard);
if (handIndex !== -1) {
uiLayer.removeChild(selectedCard);
gameLayer.addChild(selectedCard);
gameState.playerHand[handIndex] = null;
}
gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
} else {
// Card exists, but cannot merge: swap them
var swappedCard = gameState.playerPlayArea[targetSlot.row][targetSlot.col];
// Place selectedCard in target play area slot
var pos1 = getSlotPosition(targetSlot.row, targetSlot.col, true);
selectedCard.activate(pos1.x, pos1.y, true);
var handIndex = gameState.playerHand.indexOf(selectedCard);
if (handIndex !== -1) {
uiLayer.removeChild(selectedCard);
gameLayer.addChild(selectedCard);
gameState.playerHand[handIndex] = null;
}
gameState.playerPlayArea[targetSlot.row][targetSlot.col] = selectedCard;
// Place swappedCard in original position
if (originalCardPosition.area === 'player') {
var pos2 = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
swappedCard.activate(pos2.x, pos2.y, true);
gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
} else {
// original was hand
var origHandIndex = originalCardPosition.index;
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30;
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
var slotX = handStartX + origHandIndex * DEAL_SLOT_WIDTH + origHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
swappedCard.activate(slotX, slotY, false, true);
gameLayer.removeChild(swappedCard);
uiLayer.addChild(swappedCard);
gameState.playerHand[origHandIndex] = swappedCard;
}
}
} else if (targetSlot.area === 'hand') {
var existingCard = gameState.playerHand[targetSlot.index];
if (existingCard && existingCard !== selectedCard && selectedCard.canMergeWith(existingCard)) {
// Merge in hand
var mergedCard = selectedCard.mergeWith(existingCard);
if (mergedCard) {
// Remove old cards
var handIndex1 = gameState.playerHand.indexOf(selectedCard);
if (handIndex1 !== -1) {
uiLayer.removeChild(selectedCard);
gameState.playerHand[handIndex1] = null;
} else {
gameLayer.removeChild(selectedCard);
}
var handIndex2 = gameState.playerHand.indexOf(existingCard);
if (handIndex2 !== -1) {
uiLayer.removeChild(existingCard);
gameState.playerHand[handIndex2] = null;
}
// Place merged card in hand
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
var slotX = handStartX + targetSlot.index * DEAL_SLOT_WIDTH + targetSlot.index * 30 + DEAL_SLOT_WIDTH / 2;
var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
mergedCard.activate(slotX, slotY, false, true);
uiLayer.addChild(mergedCard);
gameState.playerHand[targetSlot.index] = mergedCard;
// Merge animation
mergedCard.alpha = 0;
mergedCard.scaleX = mergedCard.scaleY = 2;
tween(mergedCard, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
createFloatingText('+Level Up!', mergedCard.x, mergedCard.y - 50, 0x00ff00);
} else {
returnCardToOriginalPosition();
}
} else if (existingCard && existingCard !== selectedCard) {
// Cannot merge, so swap
var swappedCard = gameState.playerHand[targetSlot.index];
// Move selectedCard to target hand slot
var targetHandIndex = targetSlot.index;
var handWidth1 = 5 * DEAL_SLOT_WIDTH + 4 * 30;
var handStartX1 = (SCREEN_WIDTH - handWidth1) / 2;
var slotX1 = handStartX1 + targetHandIndex * DEAL_SLOT_WIDTH + targetHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
var slotY1 = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
selectedCard.activate(slotX1, slotY1, false, true);
if (originalCardPosition.area === 'player') {
gameLayer.removeChild(selectedCard);
uiLayer.addChild(selectedCard);
} else {
gameState.playerHand[originalCardPosition.index] = null;
}
gameState.playerHand[targetHandIndex] = selectedCard;
// Move swappedCard to original position
if (originalCardPosition.area === 'player') {
var origPos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
swappedCard.activate(origPos.x, origPos.y, true);
uiLayer.removeChild(swappedCard);
gameLayer.addChild(swappedCard);
gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = swappedCard;
} else {
// original was hand
var origHandIndex = originalCardPosition.index;
var handWidth2 = 5 * DEAL_SLOT_WIDTH + 4 * 30;
var handStartX2 = (SCREEN_WIDTH - handWidth2) / 2;
var slotX2 = handStartX2 + origHandIndex * DEAL_SLOT_WIDTH + origHandIndex * 30 + DEAL_SLOT_WIDTH / 2;
var slotY2 = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
swappedCard.activate(slotX2, slotY2, false, true);
gameState.playerHand[origHandIndex] = swappedCard;
}
} else {
returnCardToOriginalPosition();
}
}
} else {
returnCardToOriginalPosition();
}
selectedCard = null;
originalCardPosition = null;
applyHandBonuses();
}
};
function returnCardToOriginalPosition() {
if (!selectedCard || !originalCardPosition) {
return;
}
if (originalCardPosition.area === 'hand') {
// Return to hand slot
var handIndex = originalCardPosition.index;
var handWidth = 5 * DEAL_SLOT_WIDTH + 4 * 30; // 5 slots + 4 gaps of 30px
var handStartX = (SCREEN_WIDTH - handWidth) / 2;
var slotX = handStartX + handIndex * DEAL_SLOT_WIDTH + handIndex * 30 + DEAL_SLOT_WIDTH / 2;
var slotY = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
selectedCard.x = slotX;
selectedCard.y = slotY;
gameState.playerHand[handIndex] = selectedCard;
} else if (originalCardPosition.area === 'player') {
// Return to play area slot
var pos = getSlotPosition(originalCardPosition.row, originalCardPosition.col, true);
selectedCard.x = pos.x;
selectedCard.y = pos.y;
gameState.playerPlayArea[originalCardPosition.row][originalCardPosition.col] = selectedCard;
}
}
function createFloatingText(text, x, y, color, size) {
var floatingText = new Text2(text, {
size: size || 40,
fill: color || 0xffffff,
weight: 800
});
floatingText.anchor.set(0.5, 0.5);
floatingText.x = x;
floatingText.y = y;
floatingText.alpha = 1;
uiLayer.addChild(floatingText);
// Use larger animation for important messages
var animationDistance = size && size > 40 ? 120 : 80;
var animationDuration = size && size > 40 ? 2000 : 1500;
tween(floatingText, {
y: y - animationDistance,
alpha: 0
}, {
duration: animationDuration,
easing: tween.quadOut,
onFinish: function onFinish() {
uiLayer.removeChild(floatingText);
}
});
}
/****
* Main Game Loop
****/
game.update = function () {
// Update wave spawning system
WaveSystem.update();
// Update active chips
for (var i = activePlayerChips.length - 1; i >= 0; i--) {
activePlayerChips[i].update();
}
for (var i = activeAIChips.length - 1; i >= 0; i--) {
activeAIChips[i].update();
}
// Update active bullets
for (var i = activeBullets.length - 1; i >= 0; i--) {
activeBullets[i].update();
}
// Update cards in play
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
for (var col = 0; col < PLAY_AREA_COLS; col++) {
if (gameState.playerPlayArea[row][col]) {
gameState.playerPlayArea[row][col].update();
}
if (gameState.aiPlayArea[row][col]) {
gameState.aiPlayArea[row][col].update();
}
}
}
// Update AI
AISystem.update();
// Check win/lose conditions
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();
}
}
/****
* Utility Functions
****/
function displayHandInfo() {
// Show current poker hand evaluations for debugging
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
var handEval = evaluateRowHand(row, true);
console.log('Player Row ' + row + ':', handEval.type, 'Multiplier:', handEval.multiplier);
}
}
var background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0
});
background.x = SCREEN_WIDTH / 2;
background.y = 0;
gameLayer.addChild(background);
initializeGame();
;
; ===================================================================
--- original.js
+++ change.js
@@ -1241,34 +1241,18 @@
});
waveText.x = SCREEN_WIDTH - 200;
waveText.y = 50;
uiLayer.addChild(waveText);
-var dealButton = new Container();
-var dealButtonGraphics = dealButton.attachAsset('dealButton', {
+var discardAreaContainer = new Container();
+var discardAreaGraphic = discardAreaContainer.attachAsset('dealButton', {
anchorX: 0.5,
anchorY: 0.5
});
-var dealButtonText = new Text2('Deal (75)', {
+var discardText = new Text2('Deal (25)', {
size: 40,
fill: 0xffffff,
weight: 800
});
-dealButtonText.anchor.set(0.5, 0.5);
-dealButton.addChild(dealButtonText);
-dealButton.x = 170;
-dealButton.y = SCREEN_HEIGHT - 240;
-uiLayer.addChild(dealButton);
-var discardAreaContainer = new Container();
-var discardAreaGraphic = discardAreaContainer.attachAsset('discardArea', {
- anchorX: 0.5,
- anchorY: 0.5
-});
-discardAreaGraphic.alpha = 0.5;
-var discardText = new Text2('Discard', {
- size: 40,
- fill: 0x999999,
- weight: 800
-});
discardText.anchor.set(0.5, 0.5);
discardAreaContainer.addChild(discardText);
// Position it right of the hand
var handWidthForDiscard = 5 * DEAL_SLOT_WIDTH + 4 * 30;
@@ -1280,10 +1264,10 @@
}
discardAreaContainer.x = discardX;
discardAreaContainer.y = PLAYER_DEAL_AREA_Y + DEAL_SLOT_HEIGHT / 2;
uiLayer.addChild(discardAreaContainer);
-dealButton.down = function () {
- if (gameState.playerGold >= gameState.dealCost) {
+discardAreaContainer.down = function () {
+ if (!isDragging && gameState.playerGold >= gameState.dealCost) {
dealNewHand();
}
};
/****
@@ -1356,16 +1340,20 @@
playerGoldText.setText('Gold: ' + gameState.playerGold);
playerLivesText.setText('Lives: ' + gameState.playerLives);
aiGoldText.setText('AI Gold: ' + gameState.aiGold);
aiLivesText.setText('AI Lives: ' + gameState.aiLives);
- dealButtonText.setText('Deal (' + gameState.dealCost + ')');
- waveText.setText('Wave: ' + WaveSystem.waveNumber);
- // Update button color based on affordability
- if (gameState.playerGold >= gameState.dealCost) {
- dealButtonGraphics.tint = 0x00aa00;
- } else {
- dealButtonGraphics.tint = 0x666666;
+ // Update combined deal/discard button text and appearance when not dragging
+ if (!isDragging) {
+ discardText.setText('Deal (' + gameState.dealCost + ')');
+ discardText.fill = 0xffffff;
+ // Update button color based on affordability
+ if (gameState.playerGold >= gameState.dealCost) {
+ discardAreaGraphic.tint = 0x00aa00;
+ } else {
+ discardAreaGraphic.tint = 0x666666;
+ }
}
+ waveText.setText('Wave: ' + WaveSystem.waveNumber);
}
function drawPlayAreas() {
// Draw player play area slots
for (var row = 0; row < PLAY_AREA_ROWS; row++) {
@@ -1873,28 +1861,31 @@
game.move = function (x, y, obj) {
if (isDragging && selectedCard) {
selectedCard.x = x;
selectedCard.y = y;
+ // Switch to discard mode when dragging
+ discardAreaGraphic.tint = 0x440000; // Dark red for discard
// Highlight discard area on hover
var isOverDiscard = discardAreaContainer && x >= discardAreaContainer.x - DEAL_SLOT_WIDTH / 2 && x <= discardAreaContainer.x + DEAL_SLOT_WIDTH / 2 && y >= discardAreaContainer.y - DEAL_SLOT_HEIGHT / 2 && y <= discardAreaContainer.y + DEAL_SLOT_HEIGHT / 2;
if (isOverDiscard) {
discardAreaGraphic.alpha = 1.0;
discardText.setText('Sell Card');
discardText.fill = 0xffd700; // Gold color
} else {
- discardAreaGraphic.alpha = 0.5;
+ discardAreaGraphic.alpha = 0.7;
discardText.setText('Discard');
discardText.fill = 0x999999;
}
+ } else {
+ // When not dragging, show as deal button
+ updateUI();
}
};
game.up = function (x, y, obj) {
if (isDragging && selectedCard) {
isDragging = false;
- // Reset discard area appearance
- discardAreaGraphic.alpha = 0.5;
- discardText.setText('Discard');
- discardText.fill = 0x999999;
+ // Reset to deal button mode
+ updateUI();
var targetSlot = getSlotFromPosition(x, y);
if (targetSlot) {
// Handle dropping card on discard area
if (targetSlot.area === 'discard') {
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
Change the blue color to gold and put a blue glowing shield in the center.
Change the image of the shield in the center into a red question mark lined with gold.
Change the image of a shield in the center into a robotic cheetah head. Change the background color in the center to green.
Change the image of the shield into the center to a steel padlock with a keyhole on it. Change the background color in the center to yellow.
Change the shield in the center to a picture of a winding snake and the background color in the center in purple.
Change the word to say Slots
An icon of a slot machine. Anime style. High definition.. In-Game asset. 2d. High contrast. No shadows
A “Spin” button for an electronic slot machine. Button is red and font is in yellow. Anime style. High definition.. In-Game asset. 2d. High contrast. No shadows
Create a symbol that’s just the white cats head looking straight forward with X’s for eyes and his tongue hanging out of the side of his mouth.
Change the text to say Paytable and adjust button shape to accommodate.
Change the button to say $10 and make it square.
Change the button to say $25
Change the button to say $50
Change the button to say $100
Change the button color to light green.
Change the button color to light green.
Change the button color to light green.
Change the button color to light green.
Add an icon of the four card suits in the center of the heart. Anime style.
Just the spade from this picture with an icon of a cartoon spy with a hood and a mask on in the center of the spade.
Just the club from this picture with a small bubbling green vial of poison in the center of it. Anime style.
Just the diamond from this picture with an icon of a scale in the center.
titleSong
Music
pvpMusic
Music
getReady
Sound effect
gameStart
Sound effect
cardLand
Sound effect
shootSound
Sound effect
levelUp
Sound effect
buttonPress
Sound effect
pokerChipDie
Sound effect
roulette
Sound effect
victory
Sound effect
defeat
Sound effect
slotButton
Sound effect
slotSpin
Sound effect
reelStop
Sound effect
slotWin
Sound effect