User prompt
modify basic herbivore cards and basic carnivore cards to allow play onto advanced lands
User prompt
please allow 8 max hand size for debug purposes (will revert back to 3 at end)
User prompt
modify rules so that basic carnivores cannot be played on another basic carnivore card
User prompt
advanced terrain cards have duplicate text - please remove the 2nd line of text which appears to be a repeat of their sub-type
User prompt
reduce text size on advanced terrain and move it south a tiny bit
User prompt
the text at the top of the advanced terrain cards is overlapping, please fix
User prompt
i cannot draw. please fix
User prompt
we are ready for the next step! Player hand mechanics: player maximum hand size is 3. Player draws 3 cards from the top of the main deck during the 'draw step' then, If player has no valid placements with the 3 cards drawn, player must discard all 3 cards to the main deck discard pile and then begins a process of drawing 1 card at a time from the top of the main deck until the card can be played and then that card MUST be played. all cards drawn in this stage that cannot be played are discarded to the main deck discard pile
User prompt
make that text black
User prompt
adjust text denoting type on basic terrain to occupy a similar position to the text on advanced terrain cards
User prompt
modify rules so that basic herbivores cannot be placed upon basic carnivores
User prompt
almost there! 1 tiny increment more south again please
User prompt
much better but not quite perfect - please adjust again a little more south
User prompt
the creature cards are still being placed too high on the terrain card, at the moment they are positioned so that the bottom of the creature card is about 1 third of the way up the terrain card, please adjust so that they are much lower, just below the colour bar of the terrain card it is being played upon
User prompt
the previous modification was too much! creatures must enter play only slightly lower on the y axis than the terrain cards then are played upon, the top creature card on all stacks should be positioned just below the colour band of the terrain card
User prompt
please adjust position that creature cards on the top of the stack are placed: they must sit flush with the bottom of the colour bar located at the top of the terrain card
User prompt
perfect! you are right on both counts, please implement the new 'stack' terminology and the new card stack behaviour as you have written above
User prompt
scale up the y axis on all creature cards to a point less than they are now but more than they were in the previous step - they should sit flush with the bottom of the colour bar on the terrain card when placed
User prompt
continue to scale down the y axis of all creature cards by 1 more degree
User prompt
rescale all creature cards y axis by half the height of the colour bars on the terrain cards
User prompt
adjust the last step (text on advanced terrain cards) to include the type: 'water' or 'land' as well as the sub-type already implemented
User prompt
add text to the top of advanced terrain cards denoting what types they are (not climate) make the text bold, black and just a little shorter than half the width of the top colour bars or the terrain cards
User prompt
modify the 'req' text on creature cards to be in a higher position on the cards - above the bottom colour bar
User prompt
perfect! now enhance the pink highlight to be brighter and scale it up a little ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
now modify all cards so that the pink highlight for possible placements activates when the mouse cursor hovers above the card while in hand and when the card is being dragged as well as the existing activation when above a valid move ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var CreatureCard = Container.expand(function (creatureData) {
var self = Container.call(this);
self.creatureData = creatureData || {
type: 'basic',
dietType: 'herbivore',
name: 'Basic Herbivore',
terrainRequirement: 'land',
climateRequirement: 'any'
};
// Create creature visual
var creatureAsset;
if (self.creatureData.type === 'basic' && self.creatureData.dietType === 'herbivore') {
creatureAsset = self.attachAsset('creatureBasicHerbivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.type === 'advanced' && self.creatureData.dietType === 'herbivore') {
creatureAsset = self.attachAsset('creatureAdvancedHerbivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.type === 'basic' && self.creatureData.dietType === 'carnivore') {
creatureAsset = self.attachAsset('creatureBasicCarnivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.type === 'advanced' && self.creatureData.dietType === 'carnivore') {
creatureAsset = self.attachAsset('creatureAdvancedCarnivore', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Add creature type color band at bottom
var typeStripAsset;
if (self.creatureData.type === 'basic' && self.creatureData.dietType === 'herbivore') {
typeStripAsset = 'creatureTypeBasicHerbivore';
} else if (self.creatureData.type === 'advanced' && self.creatureData.dietType === 'herbivore') {
typeStripAsset = 'creatureTypeAdvancedHerbivore';
} else if (self.creatureData.type === 'basic' && self.creatureData.dietType === 'carnivore') {
typeStripAsset = 'creatureTypeBasicCarnivore';
} else if (self.creatureData.type === 'advanced' && self.creatureData.dietType === 'carnivore') {
typeStripAsset = 'creatureTypeAdvancedCarnivore';
}
if (typeStripAsset) {
var typeStrip = self.attachAsset(typeStripAsset, {
anchorX: 0.5,
anchorY: 0.5
});
typeStrip.y = 95; // Position at bottom of creature card
}
// Add creature name
self.nameText = new Text2(self.creatureData.name, {
size: 18,
fill: 0xFFFFFF
});
self.nameText.anchor.set(0.5, 0);
self.nameText.x = 0;
self.nameText.y = -100;
self.addChild(self.nameText);
// Add requirement text
var reqText = "Req: ";
if (self.creatureData.terrainRequirement && self.creatureData.terrainRequirement !== 'any') {
reqText += self.creatureData.terrainRequirement;
}
if (self.creatureData.climateRequirement && self.creatureData.climateRequirement !== 'any') {
if (reqText !== "Req: ") reqText += ", ";
reqText += self.creatureData.climateRequirement;
}
if (reqText !== "Req: ") {
self.requirementText = new Text2(reqText, {
size: 14,
fill: 0xFFFFFF
});
self.requirementText.anchor.set(0.5, 0);
self.requirementText.x = 0;
self.requirementText.y = 60;
self.addChild(self.requirementText);
}
self.isInPlay = false;
self.die = function () {
if (self.isInPlay) {
// Add event card to deck
addEventCardToDeck();
// Visual death effect
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
LK.getSound('creatureDie').play();
}
};
self.move = function (x, y, obj) {
if (!self.isInPlay && playerHand.indexOf(self) !== -1) {
// Check if mouse is actually over this card
var cardBounds = 80; // Half card width for hit detection
if (Math.abs(x) < cardBounds && Math.abs(y) < 110) {
// Half card height
showCardInfo(self);
showPossibleMoves(self);
} else {
// Mouse moved away from this card
if (hoveredCard === self) {
hideCardInfo();
hidePossibleMoves();
}
}
}
};
self.down = function (x, y, obj) {
if (!self.isInPlay && playerHand.indexOf(self) !== -1) {
selectedCard = self;
draggedCard = self;
originalCardPosition = {
x: self.x,
y: self.y
};
showPossibleMoves(self);
}
};
return self;
});
var PlanetSlot = Container.expand(function (slotX, slotY) {
var self = Container.call(this);
self.slotX = slotX;
self.slotY = slotY;
self.terrainCard = null;
self.isHighlighted = false;
self.pinkBorder = null;
self.highlight = function () {
if (!self.isHighlighted && self.terrainCard) {
self.isHighlighted = true;
// Create pink border around the terrain card
if (!self.pinkBorder) {
self.pinkBorder = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5
});
self.pinkBorder.tint = 0xFF1493; // Brighter deep pink color
self.pinkBorder.alpha = 0.9;
// Scale border to be larger than terrain card
self.pinkBorder.scaleX = self.terrainCard.scaleX * 1.3;
self.pinkBorder.scaleY = self.terrainCard.scaleY * 1.3;
self.pinkBorder.x = self.terrainCard.x;
self.pinkBorder.y = self.terrainCard.y;
// Add border behind terrain card
var terrainIndex = game.getChildIndex(self.terrainCard);
game.addChildAt(self.pinkBorder, terrainIndex);
}
}
};
self.unhighlight = function () {
if (self.isHighlighted) {
self.isHighlighted = false;
// Remove pink border
if (self.pinkBorder && self.pinkBorder.parent) {
self.pinkBorder.parent.removeChild(self.pinkBorder);
self.pinkBorder = null;
}
}
};
self.down = function (x, y, obj) {
if (selectedCard && selectedCard.creatureData && self.terrainCard && canPlaceCreatureOnTerrain(selectedCard, self.terrainCard)) {
placeCreatureOnStack(selectedCard, self.terrainCard, self.slotX, self.slotY);
}
};
return self;
});
var TerrainCard = Container.expand(function (terrainData) {
var self = Container.call(this);
self.terrainData = terrainData || {
type: 'basic',
subtype: 'land',
landType: 'flat',
waterType: null,
climate: 'temperate'
};
// Create terrain card visual based on subtype and landType/waterType
var terrainAsset;
if (self.terrainData.subtype === 'land') {
if (self.terrainData.landType === 'flat') {
terrainAsset = self.attachAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.terrainData.landType === 'hills') {
terrainAsset = self.attachAsset('terrainLandHills', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.terrainData.landType === 'mountain') {
terrainAsset = self.attachAsset('terrainLandMountain', {
anchorX: 0.5,
anchorY: 0.5
});
}
} else if (self.terrainData.subtype === 'water') {
if (self.terrainData.waterType === 'sea') {
terrainAsset = self.attachAsset('terrainWaterSea', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.terrainData.waterType === 'fresh') {
terrainAsset = self.attachAsset('terrainWaterFresh', {
anchorX: 0.5,
anchorY: 0.5
});
}
}
// Add terrain type indicator strip at top
var terrainStripAsset = self.terrainData.type === 'advanced' ? 'advancedTerrainStrip' : 'basicTerrainStrip';
var terrainStrip = self.attachAsset(terrainStripAsset, {
anchorX: 0.5,
anchorY: 0.5
});
terrainStrip.y = -147;
// Add terrain type text
var terrainTypeText = self.terrainData.subtype === 'land' ? self.terrainData.landType : self.terrainData.waterType;
self.typeText = new Text2(terrainTypeText.toUpperCase(), {
size: 24,
fill: 0xFFFFFF,
font: "'Arial Black', 'Impact', 'Tahoma', sans-serif"
});
self.typeText.anchor.set(0.5, 0.5);
self.typeText.x = 0;
self.typeText.y = -147;
self.addChild(self.typeText);
// Add advanced terrain type text at the top for advanced terrain cards
if (self.terrainData.type === 'advanced') {
var advancedTypeText = self.terrainData.subtype.toUpperCase() + " " + terrainTypeText.toUpperCase();
self.advancedTypeText = new Text2(advancedTypeText, {
size: 18,
fill: 0x000000,
font: "'Arial Black', 'Impact', 'Tahoma', sans-serif"
});
self.advancedTypeText.anchor.set(0.5, 0.5);
self.advancedTypeText.x = 0;
self.advancedTypeText.y = -147; // Position at top color bar
self.addChild(self.advancedTypeText);
}
// Add requirement text for advanced terrain cards
if (self.terrainData.type === 'advanced' && self.terrainData.climateRequirement) {
self.requirementText = new Text2("Req: " + self.terrainData.climateRequirement, {
size: 16,
fill: 0xFFFFFF
});
self.requirementText.anchor.set(0.5, 0);
self.requirementText.x = 0;
self.requirementText.y = 120;
self.addChild(self.requirementText);
}
// Climate will be displayed as row background instead of on individual cards
self.gridX = -1;
self.gridY = -1;
self.creatureStack = [];
self.move = function (x, y, obj) {
if (!self.isInPlay && playerHand.indexOf(self) !== -1) {
// Check if mouse is actually over this card (scaled terrain cards are smaller)
var cardBounds = 75; // Adjusted for scaled terrain cards
if (Math.abs(x) < cardBounds && Math.abs(y) < 100) {
showCardInfo(self);
showPossibleMoves(self);
} else {
// Mouse moved away from this card
if (hoveredCard === self) {
hideCardInfo();
hidePossibleMoves();
}
}
}
};
self.down = function (x, y, obj) {
if (!self.isInPlay && playerHand.indexOf(self) !== -1) {
selectedCard = self;
draggedCard = self;
originalCardPosition = {
x: self.x,
y: self.y
};
showPossibleMoves(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a252f
});
/****
* Game Code
****/
// UI elements
// Event card asset
// Creature card assets
// Climate indicator strips
// Terrain card assets - Water types with blue/grey strip
// Terrain card assets - Land types with green strip
// Game state variables
var currentRound = 0;
var maxRounds = 5;
var cardsDrawnThisTurn = 0;
var maxCardsPerTurn = 3;
var planetBoard = [];
var planetSlots = [];
var playerHand = [];
var maxHandSize = 5;
var mainDeck = [];
var basicTerrainPool = [];
var selectedCard = null;
var draggedCard = null;
var originalCardPosition = null;
var scryTokenActive = true;
var cardInfoText = null;
var hoveredCard = null;
// Planet layout: 2-4-6-4-2 formation
var planetLayout = [2, 4, 6, 4, 2];
var planetWidth = 6; // Max cards in any row
var planetHeight = 5; // Number of rows
// UI Elements
var roundText = new Text2("Round: " + currentRound, {
size: 48,
fill: 0xFFFFFF
});
roundText.anchor.set(0.5, 0);
LK.gui.top.addChild(roundText);
roundText.y = 100;
var cardsDrawnText = new Text2("Cards Drawn: " + cardsDrawnThisTurn + "/" + maxCardsPerTurn, {
size: 36,
fill: 0xFFFFFF
});
cardsDrawnText.anchor.set(0, 0);
LK.gui.topRight.addChild(cardsDrawnText);
cardsDrawnText.x = -300;
cardsDrawnText.y = 150;
var deckCountText = new Text2("Deck: " + mainDeck.length, {
size: 36,
fill: 0xFFFFFF
});
deckCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(deckCountText);
deckCountText.x = 120;
deckCountText.y = 150;
var scryText = new Text2("Scry", {
size: 32,
fill: 0x00FF00
});
scryText.anchor.set(0, 0);
LK.gui.topRight.addChild(scryText);
scryText.x = -300;
scryText.y = 190;
// Initialize basic terrain pool (23 cards total)
function createBasicTerrainPool() {
basicTerrainPool = [];
// Add 9 water cards: 6 sea, 3 fresh
for (var i = 0; i < 6; i++) {
basicTerrainPool.push({
type: 'basic',
subtype: 'water',
waterType: 'sea',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 3; i++) {
basicTerrainPool.push({
type: 'basic',
subtype: 'water',
waterType: 'fresh',
climate: null // Will be assigned during setup
});
}
// Add 14 land cards: 4 mountain, 5 hills, 5 flat
for (var i = 0; i < 4; i++) {
basicTerrainPool.push({
type: 'basic',
subtype: 'land',
landType: 'mountain',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 5; i++) {
basicTerrainPool.push({
type: 'basic',
subtype: 'land',
landType: 'hills',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 5; i++) {
basicTerrainPool.push({
type: 'basic',
subtype: 'land',
landType: 'flat',
climate: null // Will be assigned during setup
});
}
// Shuffle terrain pool
for (var i = basicTerrainPool.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = basicTerrainPool[i];
basicTerrainPool[i] = basicTerrainPool[j];
basicTerrainPool[j] = temp;
}
}
// Setup planet board with terrain cards
function setupPlanet() {
// Create 2D array for planet board
for (var y = 0; y < planetHeight; y++) {
planetBoard[y] = [];
planetSlots[y] = [];
for (var x = 0; x < planetWidth; x++) {
planetBoard[y][x] = null;
planetSlots[y][x] = null;
}
}
// Place terrain cards according to 2-4-6-4-2 layout
var terrainIndex = 0;
var startY = 400;
var cardSpacing = 320;
// Add climate row backgrounds first
for (var row = 0; row < planetLayout.length; row++) {
var climate;
if (row === 0 || row === 4) {
climate = 'cold'; // Rows 1 & 5
} else if (row === 1 || row === 3) {
climate = 'temperate'; // Rows 2 & 4
} else {
climate = 'hot'; // Row 3
}
// Create climate row background
var climateRowAsset = 'climateRow' + climate.charAt(0).toUpperCase() + climate.slice(1);
var climateRowBg = LK.getAsset(climateRowAsset, {
anchorX: 0.5,
anchorY: 0.5
});
climateRowBg.x = 1024; // Center of screen
climateRowBg.y = startY + row * 420;
climateRowBg.alpha = 0.3; // Semi-transparent background
game.addChild(climateRowBg);
// Add climate label text
var climateLabel = new Text2(climate.toUpperCase(), {
size: 32,
fill: 0x000000,
font: "'Arial Black', 'Impact', 'Tahoma', sans-serif"
});
climateLabel.anchor.set(0.5, 0.5);
climateLabel.rotation = -Math.PI / 2; // Rotate 90 degrees counter-clockwise to make vertical
climateLabel.x = 50; // Move left to make fully visible
climateLabel.y = startY + row * 420;
game.addChild(climateLabel);
}
for (var row = 0; row < planetLayout.length; row++) {
var cardsInRow = planetLayout[row];
var startX = 1024 - cardsInRow * cardSpacing / 2 + cardSpacing / 2 + 50; // Shift cards right by 50px
// Assign climate based on row
var climate;
if (row === 0 || row === 4) {
climate = 'cold'; // Rows 1 & 5
} else if (row === 1 || row === 3) {
climate = 'temperate'; // Rows 2 & 4
} else {
climate = 'hot'; // Row 3
}
for (var col = 0; col < cardsInRow && terrainIndex < basicTerrainPool.length; col++) {
// Assign climate to terrain card
var terrainData = basicTerrainPool[terrainIndex];
terrainData.climate = climate;
// Create terrain card
var terrainCard = new TerrainCard(terrainData);
terrainCard.x = startX + col * cardSpacing;
terrainCard.y = startY + row * 420;
terrainCard.gridX = col;
terrainCard.gridY = row;
game.addChild(terrainCard);
// Store in board
var boardX = Math.floor((planetWidth - cardsInRow) / 2) + col;
planetBoard[row][boardX] = terrainCard;
// Create slot for this position
var slot = new PlanetSlot(boardX, row);
slot.terrainCard = terrainCard;
planetSlots[row][boardX] = slot;
terrainIndex++;
}
}
}
// Create main deck with advanced terrain cards and creature cards
function createInitialDeck() {
mainDeck = [];
// Add 20 Advanced Terrain Cards
// 2x Fresh Water (no climate requirement)
for (var i = 0; i < 2; i++) {
mainDeck.push({
type: 'advanced',
cardType: 'terrain',
subtype: 'water',
waterType: 'fresh',
name: 'Advanced Fresh Water ' + (i + 1),
climateRequirement: 'any'
});
}
// 5x Sea Water (1 cold, 1 hot, 1 temperate, 1 temperate/hot, 1 any)
var seaClimateReqs = ['cold', 'hot', 'temperate', 'temperate/hot', 'any'];
for (var i = 0; i < 5; i++) {
mainDeck.push({
type: 'advanced',
cardType: 'terrain',
subtype: 'water',
waterType: 'sea',
name: 'Advanced Sea Water ' + (i + 1),
climateRequirement: seaClimateReqs[i]
});
}
// 3x Mountain (1 any, 1 temperate/cold, 1 temperate/hot)
var mountainClimateReqs = ['any', 'temperate/cold', 'temperate/hot'];
for (var i = 0; i < 3; i++) {
mainDeck.push({
type: 'advanced',
cardType: 'terrain',
subtype: 'land',
landType: 'mountain',
name: 'Advanced Mountain ' + (i + 1),
climateRequirement: mountainClimateReqs[i]
});
}
// 5x Hills (1 hot, 1 cold, 1 temperate, 1 temperate/hot, 1 temperate/cold)
var hillsClimateReqs = ['hot', 'cold', 'temperate', 'temperate/hot', 'temperate/cold'];
for (var i = 0; i < 5; i++) {
mainDeck.push({
type: 'advanced',
cardType: 'terrain',
subtype: 'land',
landType: 'hills',
name: 'Advanced Hills ' + (i + 1),
climateRequirement: hillsClimateReqs[i]
});
}
// 5x Flat Land (1 cold, 1 temperate, 1 hot, 1 temperate/hot, 1 temperate/cold)
var flatClimateReqs = ['cold', 'temperate', 'hot', 'temperate/hot', 'temperate/cold'];
for (var i = 0; i < 5; i++) {
mainDeck.push({
type: 'advanced',
cardType: 'terrain',
subtype: 'land',
landType: 'flat',
name: 'Advanced Flat Land ' + (i + 1),
climateRequirement: flatClimateReqs[i]
});
}
// Add 32 Basic Herbivore Cards
var herbivoreClimateReqs = ['cold', 'temperate', 'hot', 'temperate/hot', 'temperate/cold', 'any'];
for (var i = 0; i < 32; i++) {
var climateReq = herbivoreClimateReqs[i % herbivoreClimateReqs.length];
mainDeck.push({
type: 'basic',
cardType: 'creature',
dietType: 'herbivore',
name: 'Basic Herbivore ' + (i + 1),
terrainRequirement: 'land',
climateRequirement: climateReq
});
}
// Add 12 Advanced Herbivore Cards
var advHerbClimateReqs = ['cold', 'temperate', 'hot', 'temperate/hot'];
for (var i = 0; i < 12; i++) {
var climateReq = advHerbClimateReqs[i % advHerbClimateReqs.length];
mainDeck.push({
type: 'advanced',
cardType: 'creature',
dietType: 'herbivore',
name: 'Advanced Herbivore ' + (i + 1),
terrainRequirement: 'land',
climateRequirement: climateReq
});
}
// Add 10 Basic Carnivore Cards
var carnivoreClimateReqs = ['cold', 'temperate', 'hot', 'temperate/cold', 'any'];
for (var i = 0; i < 10; i++) {
var climateReq = carnivoreClimateReqs[i % carnivoreClimateReqs.length];
mainDeck.push({
type: 'basic',
cardType: 'creature',
dietType: 'carnivore',
name: 'Basic Carnivore ' + (i + 1),
terrainRequirement: 'any',
climateRequirement: climateReq
});
}
// Add 8 Advanced Carnivore Cards
var advCarnClimateReqs = ['cold', 'hot', 'temperate/hot', 'any'];
for (var i = 0; i < 8; i++) {
var climateReq = advCarnClimateReqs[i % advCarnClimateReqs.length];
mainDeck.push({
type: 'advanced',
cardType: 'creature',
dietType: 'carnivore',
name: 'Advanced Carnivore ' + (i + 1),
terrainRequirement: 'any',
climateRequirement: climateReq
});
}
// Shuffle deck
shuffleDeck();
}
function shuffleDeck() {
for (var i = mainDeck.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = mainDeck[i];
mainDeck[i] = mainDeck[j];
mainDeck[j] = temp;
}
}
function showCardInfo(card) {
hoveredCard = card;
// Remove existing info text if any
if (cardInfoText && cardInfoText.parent) {
cardInfoText.parent.removeChild(cardInfoText);
}
var infoString = "";
if (card.creatureData) {
// Creature card information
var creature = card.creatureData;
if (creature) {
infoString = creature.name + " | Type: " + creature.type + " " + creature.dietType;
if (creature && creature.terrainRequirement && creature.terrainRequirement !== 'any') {
infoString += " | Terrain: " + creature.terrainRequirement;
}
if (creature && creature.climateRequirement && creature.climateRequirement !== 'any') {
infoString += " | Climate: " + creature.climateRequirement;
}
}
} else if (card.terrainData) {
// Terrain card information
var terrain = card.terrainData;
if (terrain) {
infoString = terrain.name || terrain.type + " " + (terrain.landType || terrain.waterType);
infoString += " | Type: " + terrain.type + " terrain";
if (terrain.climateRequirement && terrain.climateRequirement !== 'any') {
infoString += " | Climate Required: " + terrain.climateRequirement;
}
}
}
// Create info text at bottom of screen
cardInfoText = new Text2(infoString, {
size: 28,
fill: 0xFFFFFF
});
cardInfoText.anchor.set(0.5, 1);
cardInfoText.x = 1024; // Center of screen
cardInfoText.y = 2700; // Bottom of screen
game.addChild(cardInfoText);
}
function hideCardInfo() {
if (cardInfoText && cardInfoText.parent) {
cardInfoText.parent.removeChild(cardInfoText);
cardInfoText = null;
}
hoveredCard = null;
}
function showPossibleMoves(card) {
if (card.creatureData) {
// Highlight valid terrain placement for creatures
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && canPlaceCreatureOnTerrain(card, terrain)) {
planetSlots[gridY][gridX].highlight();
}
}
}
} else if (card.terrainData) {
// Highlight valid terrain placement for advanced terrain
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var existingTerrain = planetBoard[gridY][gridX];
if (existingTerrain && canPlaceTerrainOnTerrain(card, existingTerrain)) {
planetSlots[gridY][gridX].highlight();
}
}
}
}
}
function hidePossibleMoves() {
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
if (planetSlots[gridY][gridX]) {
planetSlots[gridY][gridX].unhighlight();
}
}
}
}
function drawCard() {
if (cardsDrawnThisTurn >= maxCardsPerTurn) {
return null;
}
if (mainDeck.length === 0) {
endRound();
return null;
}
if (playerHand.length >= maxHandSize) {
return null;
}
var cardData = mainDeck.pop();
var card;
// Create appropriate card type based on cardType
if (cardData.cardType === 'terrain') {
card = new TerrainCard(cardData);
// Scale terrain cards in hand to match creature cards
card.scaleX = 0.6;
card.scaleY = 0.6;
} else {
// Default to creature card
card = new CreatureCard(cardData);
}
playerHand.push(card);
cardsDrawnThisTurn++;
// Position card in hand
updateHandPositions();
game.addChild(card);
// Update UI
cardsDrawnText.setText("Cards Drawn: " + cardsDrawnThisTurn + "/" + maxCardsPerTurn);
deckCountText.setText("Deck: " + mainDeck.length);
LK.getSound('cardDraw').play();
return card;
}
function updateHandPositions() {
var handY = 2400;
var handStartX = 1024 - playerHand.length * 200 / 2 + 100;
for (var i = 0; i < playerHand.length; i++) {
var card = playerHand[i];
if (!card.isInPlay) {
tween(card, {
x: handStartX + i * 200,
y: handY
}, {
duration: 300
});
}
}
}
function canPlaceTerrainOnTerrain(advancedTerrainCard, basicTerrainCard) {
if (!advancedTerrainCard || !basicTerrainCard) return false;
var advancedTerrain = advancedTerrainCard.terrainData;
var basicTerrain = basicTerrainCard.terrainData;
// Check if this is actually an advanced terrain card
if (!advancedTerrain || advancedTerrain.type !== 'advanced') return false;
// Check if target is a basic terrain card
if (!basicTerrain || basicTerrain.type !== 'basic') return false;
// Check if terrain types match (land on land, water on water)
if (advancedTerrain.subtype !== basicTerrain.subtype) return false;
// Check if specific terrain types match
if (advancedTerrain.subtype === 'land') {
if (advancedTerrain.landType !== basicTerrain.landType) return false;
} else if (advancedTerrain.subtype === 'water') {
if (advancedTerrain.waterType !== basicTerrain.waterType) return false;
}
// Check climate requirements
if (advancedTerrain.climateRequirement && advancedTerrain.climateRequirement !== 'any') {
var climateMatches = false;
if (advancedTerrain.climateRequirement.indexOf('/') !== -1) {
// Handle multiple climate requirements (e.g., "temperate/hot")
var allowedClimates = advancedTerrain.climateRequirement.split('/');
for (var k = 0; k < allowedClimates.length; k++) {
if (allowedClimates[k] === basicTerrain.climate) {
climateMatches = true;
break;
}
}
} else {
// Single climate requirement
climateMatches = advancedTerrain.climateRequirement === basicTerrain.climate;
}
if (!climateMatches) return false;
}
return true;
}
function canPlaceCreatureOnTerrain(creatureCard, terrainCard) {
if (!creatureCard || !terrainCard) return false;
var creature = creatureCard.creatureData;
var terrain = terrainCard.terrainData;
// Check if this is actually a creature card
if (!creature) return false;
// Check basic herbivore placement rules - cannot be placed on stacks with other basic herbivores at top OR basic carnivores at top
if (creature.type === 'basic' && creature.dietType === 'herbivore') {
if (terrainCard.creatureStack.length > 0) {
var topCreatureInStack = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
if (topCreatureInStack.creatureData.type === 'basic' && topCreatureInStack.creatureData.dietType === 'herbivore') {
return false; // Basic herbivores cannot be placed on stacks with other basic herbivores at top
}
if (topCreatureInStack.creatureData.type === 'basic' && topCreatureInStack.creatureData.dietType === 'carnivore') {
return false; // Basic herbivores cannot be placed on stacks with basic carnivores at top
}
}
}
// Check advanced creature placement rules first
if (creature.type === 'advanced') {
// Advanced creatures require advanced terrain underneath
if (terrain.type !== 'advanced') return false;
// Advanced herbivores can only be placed on stacks with basic herbivores at top
if (creature.dietType === 'herbivore') {
if (terrainCard.creatureStack.length === 0) return false;
var topCreatureInStack = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
if (topCreatureInStack.creatureData.type !== 'basic' || topCreatureInStack.creatureData.dietType !== 'herbivore') return false;
}
// Advanced carnivores can be placed on stacks with basic carnivores OR advanced herbivores at top
else if (creature.dietType === 'carnivore') {
if (terrainCard.creatureStack.length === 0) return false;
var topCreatureInStack = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
var validTarget = topCreatureInStack.creatureData.type === 'basic' && topCreatureInStack.creatureData.dietType === 'carnivore' || topCreatureInStack.creatureData.type === 'advanced' && topCreatureInStack.creatureData.dietType === 'herbivore';
if (!validTarget) return false;
}
}
// Check terrain requirements
if (creature.terrainRequirement !== 'any') {
if (creature.terrainRequirement === 'land' && terrain.subtype !== 'land') return false;
if (creature.terrainRequirement === 'water' && terrain.subtype !== 'water') return false;
}
// Check climate requirements
if (creature.climateRequirement !== 'any') {
var climateMatches = false;
if (creature.climateRequirement.indexOf('/') !== -1) {
// Handle multiple climate requirements (e.g., "temperate/hot")
var allowedClimates = creature.climateRequirement.split('/');
for (var k = 0; k < allowedClimates.length; k++) {
if (allowedClimates[k] === terrain.climate) {
climateMatches = true;
break;
}
}
} else {
// Single climate requirement
climateMatches = creature.climateRequirement === terrain.climate;
}
if (!climateMatches) return false;
}
return true;
}
function placeTerrainOnTerrain(advancedTerrainCard, basicTerrainCard, gridX, gridY) {
if (!canPlaceTerrainOnTerrain(advancedTerrainCard, basicTerrainCard)) {
return false;
}
// Remove card from hand
var handIndex = playerHand.indexOf(advancedTerrainCard);
if (handIndex !== -1) {
playerHand.splice(handIndex, 1);
}
// Set up advanced terrain card
advancedTerrainCard.gridX = gridX;
advancedTerrainCard.gridY = gridY;
advancedTerrainCard.isInPlay = true;
// Copy creature stack from basic terrain to advanced terrain
advancedTerrainCard.creatureStack = basicTerrainCard.creatureStack || [];
// Position advanced terrain card on top of basic terrain and adopt its dimensions
var targetX = basicTerrainCard.x;
var targetY = basicTerrainCard.y;
var targetScaleX = basicTerrainCard.scaleX;
var targetScaleY = basicTerrainCard.scaleY;
tween(advancedTerrainCard, {
x: targetX,
y: targetY,
scaleX: targetScaleX,
scaleY: targetScaleY
}, {
duration: 300
});
// Keep basic terrain underneath but update board references to advanced terrain
planetBoard[gridY][gridX] = advancedTerrainCard;
planetSlots[gridY][gridX].terrainCard = advancedTerrainCard;
// Store reference to basic terrain underneath
advancedTerrainCard.basicTerrainUnderneath = basicTerrainCard;
// Add advanced terrain to game at specific z-index (above basic terrain but below creatures)
var basicTerrainIndex = game.getChildIndex(basicTerrainCard);
game.addChildAt(advancedTerrainCard, basicTerrainIndex + 1);
// Update creature positions if any exist and ensure they stay on top
for (var i = 0; i < advancedTerrainCard.creatureStack.length; i++) {
var creature = advancedTerrainCard.creatureStack[i];
// Store current position to prevent unwanted movement
var currentX = creature.x;
var currentY = creature.y;
// Remove and re-add creature to ensure it's on top
game.removeChild(creature);
game.addChild(creature);
// Keep creature at its current position - no animation needed
creature.x = currentX;
creature.y = currentY;
}
// Update hand positions
updateHandPositions();
// Clear selection
selectedCard = null;
draggedCard = null;
// Reset turn
cardsDrawnThisTurn = 0;
cardsDrawnText.setText("Cards Drawn: " + cardsDrawnThisTurn + "/" + maxCardsPerTurn);
LK.getSound('cardPlace').play();
return true;
}
function placeCreatureOnStack(creatureCard, terrainCard, gridX, gridY) {
if (!canPlaceCreatureOnTerrain(creatureCard, terrainCard)) {
return false;
}
// Remove card from hand
var handIndex = playerHand.indexOf(creatureCard);
if (handIndex !== -1) {
playerHand.splice(handIndex, 1);
}
// Push existing creatures down in the stack (move them south)
var stackOffset = 20;
for (var i = 0; i < terrainCard.creatureStack.length; i++) {
var existingCreature = terrainCard.creatureStack[i];
tween(existingCreature, {
y: existingCreature.y + stackOffset
}, {
duration: 200
});
}
// Add new creature to top of stack
terrainCard.creatureStack.push(creatureCard);
creatureCard.gridX = gridX;
creatureCard.gridY = gridY;
creatureCard.isInPlay = true;
// Position new creature at the top position (where first creature would go)
var targetX = terrainCard.x;
var colorBarHeight = 42;
var targetY = terrainCard.y - terrainCard.height * terrainCard.scaleY / 2 + colorBarHeight + 150; // Position even lower, just below the color bar with 150px offset
// Calculate scale needed to match terrain card dimensions completely
var terrainWidth = 252; // Terrain card asset width
var terrainHeight = 336; // Terrain card asset height
var creatureWidth = 160; // Creature card asset width
var creatureHeight = 220; // Creature card asset height
// Scale to match terrain dimensions exactly, but reduce Y scale by half the color bar height with moderate scaling
var targetScaleX = terrainWidth / creatureWidth * terrainCard.scaleX;
var targetScaleY = (terrainHeight - colorBarHeight / 2) / creatureHeight * terrainCard.scaleY * 0.95; // Moderate 5% reduction to sit flush with color bar
// Start with small scale and animate up to target scale like advanced terrain
creatureCard.scaleX = 0.1;
creatureCard.scaleY = 0.1;
tween(creatureCard, {
x: targetX,
y: targetY,
scaleX: targetScaleX,
scaleY: targetScaleY
}, {
duration: 300
});
// Apply creature effects
applyCreatureEffect(creatureCard);
// Update hand positions
updateHandPositions();
// Clear selection
selectedCard = null;
draggedCard = null;
// Reset turn
cardsDrawnThisTurn = 0;
cardsDrawnText.setText("Cards Drawn: " + cardsDrawnThisTurn + "/" + maxCardsPerTurn);
LK.getSound('cardPlace').play();
return true;
}
function applyCreatureEffect(creatureCard) {
var creature = creatureCard.creatureData;
// Stack effects can be implemented here without health mechanics
}
function getAdjacentTerrains(gridX, gridY) {
var neighbors = [];
var directions = [{
x: -1,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: -1
}, {
x: 0,
y: 1
}];
for (var i = 0; i < directions.length; i++) {
var newX = gridX + directions[i].x;
var newY = gridY + directions[i].y;
if (newX >= 0 && newX < planetWidth && newY >= 0 && newY < planetHeight) {
neighbors.push(planetBoard[newY][newX]);
}
}
return neighbors;
}
function addEventCardToDeck() {
var eventCard = {
type: 'event',
name: 'Environmental Change',
effect: 'disaster'
};
// Insert event card randomly in deck
var insertIndex = Math.floor(Math.random() * (mainDeck.length + 1));
mainDeck.splice(insertIndex, 0, eventCard);
deckCountText.setText("Deck: " + mainDeck.length);
}
function endRound() {
currentRound++;
if (currentRound > maxRounds) {
// Game complete
LK.showYouWin();
return;
}
// Reshuffle deck
createInitialDeck();
// Reset turn counter
cardsDrawnThisTurn = 0;
// Update UI
roundText.setText("Round: " + currentRound);
cardsDrawnText.setText("Cards Drawn: " + cardsDrawnThisTurn + "/" + maxCardsPerTurn);
deckCountText.setText("Deck: " + mainDeck.length);
}
// Draw card button functionality
var drawButton = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
var drawButtonText = new Text2("DRAW", {
size: 32,
fill: 0xFFFFFF
});
drawButtonText.anchor.set(0.5, 0.5);
drawButton.addChild(drawButtonText);
drawButton.x = 1700;
drawButton.y = 2400;
game.addChild(drawButton);
drawButton.down = function () {
drawCard();
};
// Game move handler
game.move = function (x, y, obj) {
if (draggedCard) {
draggedCard.x = x;
draggedCard.y = y;
// Ensure dragged card is always on top
game.removeChild(draggedCard);
game.addChild(draggedCard);
// Highlights are already shown from the card's down event
} else {
// Check if mouse is over hand area, if not, hide card info
var handY = 2400;
var inHandArea = y > handY - 150 && y < handY + 150;
if (!inHandArea) {
hideCardInfo();
hidePossibleMoves();
}
}
};
game.up = function (x, y, obj) {
if (draggedCard) {
var placed = false;
// Check if card was dropped on valid terrain
for (var gridY = 0; gridY < planetHeight && !placed; gridY++) {
for (var gridX = 0; gridX < planetWidth && !placed; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain) {
if (draggedCard.creatureData && canPlaceCreatureOnTerrain(draggedCard, terrain)) {
if (Math.abs(terrain.x - x) < 140 && Math.abs(terrain.y - y) < 168) {
placed = placeCreatureOnStack(draggedCard, terrain, gridX, gridY);
}
} else if (draggedCard.terrainData && canPlaceTerrainOnTerrain(draggedCard, terrain)) {
if (Math.abs(terrain.x - x) < 140 && Math.abs(terrain.y - y) < 168) {
placed = placeTerrainOnTerrain(draggedCard, terrain, gridX, gridY);
}
}
}
}
}
// If not placed, return to original position
if (!placed && originalCardPosition) {
tween(draggedCard, originalCardPosition, {
duration: 300
});
}
// Clear all highlights
hidePossibleMoves();
draggedCard = null;
originalCardPosition = null;
}
};
// Initialize terrain-based game
createBasicTerrainPool();
setupPlanet();
createInitialDeck();
// Round starts at 0, no initial cards drawn
currentRound = 0;
roundText.setText("Round: " + currentRound);
game.update = function () {
// Update UI elements
deckCountText.setText("Deck: " + mainDeck.length);
// Update scry token display
if (scryTokenActive) {
scryText.setText("Scry");
scryText.fill = 0x00FF00;
} else {
scryText.setText("Scry");
scryText.fill = 0xFF0000;
}
// Check round end conditions
if (mainDeck.length === 0 && playerHand.length === 0) {
endRound();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -237,9 +237,9 @@
font: "'Arial Black', 'Impact', 'Tahoma', sans-serif"
});
self.typeText.anchor.set(0.5, 0.5);
self.typeText.x = 0;
- self.typeText.y = -84;
+ self.typeText.y = -147;
self.addChild(self.typeText);
// Add advanced terrain type text at the top for advanced terrain cards
if (self.terrainData.type === 'advanced') {
var advancedTypeText = self.terrainData.subtype.toUpperCase() + " " + terrainTypeText.toUpperCase();