User prompt
make the second link line (the straight line that is generated between two creatures when a link is made) a different colour for easy visualisation
User prompt
implement: Off-set the second link marker line that advanced carnivores can make so that if an advanced carnivore creates 2 links to the same creature, the second is above (if horizontal) or to the right (if vertical) to enable visualisation of both links rather than the stacking effect that is currently happening. The player must see both link lines clearly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
implement new mechanic: When the player hovers over the 'next phase' button, if there are any creatures on the board that will receive an extinction marker this turn they should highlight in yellow and a warning text box should appear saying "Warning! These creatures are in danger! Are you sure you want to end the turn?". This text box does not stop the player from pressing 'next phase' and in no way affects the flow of the game after it appears. it disappears as soon as the player presses the 'next phase' button
User prompt
fix: Creatures underneath other creatures should not accrue markers of any kind neither should they go extinct. At the moment, a basic carnivore underneath an advanced carnivore for some reason checks its link state and accrues extinction markers even though it is no longer in play. please make it so that once a creature is underneath another creature it cannot be effected by anything unless a card specifically says so
User prompt
implement: only show the 'Adjust links' button from the Dusk phase until the player clicks 'next phase'.
User prompt
implement: When a creature makes 2 links to the same creature (usually only an advanced carnivore can do this), add a single line of text inside the link marker that links them with the text: "X2" so that the player can see these 'double links'.
User prompt
implement: When any carnivore is played onto another creature on the board, all links too and from the creature it is being played onto (whether carnivore or herbivore) must fall off.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (terrain.level === 'Advanced' && terrain.creatureStack.length > 0) {' Line Number: 1231
User prompt
Fix: i can play a basic herbivore onto an advanced carnivore for some reason! please fix this so that nothing can be played onto an advanced carnivore. Also with evolving a card into an advanced carnivore, please make any and all links on the card it is being played onto fall off as they are no longer valid. Finally for the advanced carnivore adjustments: Please make it so that if 2 identical links are made from an advanced carnivore to the same card, an icon appears on the link itself that shows clearly it is 2 links because at the moment it just looks like 1 link. perhaps a "X2" icon? thanks
User prompt
Fix: i can play a basic herbivore onto an advanced carnivore for some reason! please fix this so that nothing can be played onto an advanced herbivore. Also with evolving a card into an advanced carnivore, please make any and all links on the card it is being played onto fall off as they are no longer valid. Finally for the advanced carnivore adjustments: Please make it so that if 2 identical links are made from an advanced carnivore to the same card, an icon appears on the link itself that shows clearly it is 2 links because at the moment it just looks like 1 link. perhaps a "X2" icon? thanks
User prompt
The 'alter links' function must be adjusted since when dealing with an advanced carnivore which has 2 links to set, pressing once makes both links fall off and then the player can place 1 link but then when trying to press the other link and thus set both links, the game makes the 1st link fall off again and so it is impossible at the moment to complete both links so, Instead of clicking on the 'alter links' button to begin the process do this: when the dusk phase begins then the 'alter links' button should appear but it is just a visual reminder and is not clickable, upon the appearance of the 'alter links' button all carnivores and link generating events gain a clickable button at the centre of the card which reads: "Alter Links". clicking on a creatures personal 'alter links' button then makes all the links fall off for that creature and this can be repeated on as many cards as the player likes before they choose to then alter any links in any viable way. The highlighting of available links only becomes active when the player clicks and holds a link and stays active from that card until the player lets go of the link rather than the highlighting being active for whatever card the player last clicked. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the herbivores still generate an extinction marker when they should not. This only occurs on herbivores that have received an extinction marker on the previous turn due to having 'too many links' so the trigger must be either: Having an extinction marker already, or having too many links up until the night phase of that turn. with that in mind please fix the issue. if you cannot, we may have to reset the mechanic
User prompt
it still does it. please find and fix this issue!
User prompt
it is still happening that the herbivore receives another extinction mark when it should not. perhaps the problem is, that at the start of the turn and during noon (so until the links have been altered that turn) the herbivore was over the threshold?
User prompt
herbivore is bugged: during night phase a herbivore which has an extinction marker upon it receives another 1 when it should not. please alter the night phase and herbivores so that the herbivore only gets an extinction marker if it has too many links (so the minimum would be 1 link required). Maybe by adding a "minimum number of links upon this creature" to trigger an extinction marker state?
User prompt
we still have issues with the scry function: It still becomes active during round 2 even if, during round 1 the player has had creatures go extinct. the other issue is that when the scry function is activated, if 1 or more 'event' cards are revealed by the scry function, after the scry is then completed and the cards are returned, the players hand functions as if all the cards are 'event' cards and thus the player has to play all 3 of the cards in their hand even though they are not events. please evaluate the scry function and fix the above
User prompt
there is an error with the herbivores and their extinction marker mechanics: If a herbivore receives an extinction marker due to 'over-feeding' upon it (too many links to it), then for some reason it continue to generate extinction markers next turn even if the 'over-feeding' issue is sorted out and thus it should not receive another marker at all. it still gets an extinction marker even if there are no links at all on it! It may be that a herbivore with 1 extinction marker for some reason is triggering the generation of another 1? please evaluate and fix
User prompt
new mechanic to add - Event cards in player hand: If player has 1 or more event cards in hand then no other cards may be played. the player can only select the event cards and they must be selected and played 1 by 1 in any order before the 'Next phase' button can be pressed (The 'Next phase' button should change colour to red and read "Play events 1st!"). please implement
User prompt
another slight error is with the scry function. It currently activates on round 2 even if the player has lost creatures due to extinction markers. this should not happen. If player loses 1 or more creatures during the previous round then the scry marker is not activated and the scry function does not become available next round. please evaluate and fix
User prompt
1 new error found: When a herbivore has too many links and generates an extinction marker during the end phase of a turn, the next turn - even when the link situation has been resolved and the herbivore is no longer suffering from too many links - the game still puts another extinction marker on that herbivore and so it dies, this is wrong. Perhaps the state check for links is wrong on a herbivore after it gets an extinction marker or once it triggers the 'too many links' variable? please evaluate and fix
User prompt
please remove the 'cards drawn' element from the UI and adjust where the yellow text appears that says what phase of the turn we are on from the centre of the board to the bottom middle part - just below the players hand
User prompt
well done, continue with the second part of the implementation
User prompt
yes exactly! please implement as you have written above - well done! But please split this into 2 halves since the last 2 times i have asked you to go ahead you have crashed! so lighten the load for yourself ok
User prompt
next thing to clean up is how links function when a card is placed atop of a creature card with links from or to it: Carnivores - any links from a carnivore card that becomes covered with another card (evolved or devoured or an event) should "fall off" in the same way that extinction markers do. I believe that this already happens correctly but please check. Herbivores - Any links to a basic herbivore that evolves into an advanced herbivore should "fall off", this definitely needs to be implemented because at the moment the links do not fall off and they should fall off. Thanks
User prompt
please ensure that when cards go into `discardPile`, they are stored as **raw card data** (the object with `creatureData`, `terrainData`, `eventData` properties), rather than as a Card instance. So that, when reshuffling the discard into mainDeck, it is working with consistent data types throughout the loop.
/****
* 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',
level: 'Basic',
dietType: 'herbivore',
name: 'Basic Herbivore',
terrainRequirement: 'land',
climateRequirement: 'any'
};
// Create creature visual
var creatureAsset;
if (self.creatureData.level === 'Basic' && self.creatureData.dietType === 'herbivore') {
creatureAsset = self.attachAsset('creatureBasicHerbivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.level === 'Advanced' && self.creatureData.dietType === 'herbivore') {
creatureAsset = self.attachAsset('creatureAdvancedHerbivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.level === 'Basic' && self.creatureData.dietType === 'carnivore') {
creatureAsset = self.attachAsset('creatureBasicCarnivore', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.creatureData.level === '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.level === 'Basic' && self.creatureData.dietType === 'herbivore') {
typeStripAsset = 'creatureTypeBasicHerbivore';
} else if (self.creatureData.level === 'Advanced' && self.creatureData.dietType === 'herbivore') {
typeStripAsset = 'creatureTypeAdvancedHerbivore';
} else if (self.creatureData.level === 'Basic' && self.creatureData.dietType === 'carnivore') {
typeStripAsset = 'creatureTypeBasicCarnivore';
} else if (self.creatureData.level === '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.linkMarkers = [];
self.activeLinks = [];
self.extinctionMarkers = 0;
self.linkRequirement = 0;
self.safeLinks = 0;
// Set link requirements and safe link levels based on creature type
if (self.creatureData.dietType === 'carnivore') {
if (self.creatureData.level === 'Basic') {
self.linkRequirement = 1; // Basic carnivores need 1 link
} else if (self.creatureData.level === 'Advanced') {
self.linkRequirement = 2; // Advanced carnivores need 2 links
}
} else if (self.creatureData.dietType === 'herbivore') {
if (self.creatureData.level === 'Basic') {
self.safeLinks = 1; // Basic herbivores can safely handle 1 link
} else if (self.creatureData.level === 'Advanced') {
self.safeLinks = 2; // Advanced herbivores can safely handle 2 links
}
}
// Create visual link markers for carnivores
self.createLinkMarkers = function () {
if (self.creatureData.dietType === 'carnivore' && self.linkRequirement > 0) {
for (var i = 0; i < self.linkRequirement; i++) {
var linkMarker = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15
});
linkMarker.tint = 0xFF0000; // Red color for unlinked markers
linkMarker.x = (i - (self.linkRequirement - 1) / 2) * 25;
linkMarker.y = -80;
linkMarker.isLinked = false;
linkMarker.targetHerbivore = null;
linkMarker.carnivore = self;
linkMarker.markerIndex = i;
// Link markers are no longer directly interactive
linkMarker.move = function (x, y, obj) {};
linkMarker.down = function (x, y, obj) {};
self.addChild(linkMarker);
self.linkMarkers.push(linkMarker);
}
// Create personal "Alter Links" button for this carnivore
self.createPersonalAlterLinksButton = function () {
if (self.isInPlay) {
var alterButton = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.2
});
alterButton.tint = 0xFFD700; // Gold color
var alterButtonText = new Text2("Alter Links", {
size: 12,
fill: 0x000000
});
alterButtonText.anchor.set(0.5, 0.5);
alterButton.addChild(alterButtonText);
alterButton.x = 0;
alterButton.y = 0;
alterButton.carnivoreCard = self;
alterButton.down = function () {
if (gamePhase === 'dusk' && linkAdjustmentMode) {
resetCardLinks(self);
}
};
self.addChild(alterButton);
self.personalAlterButton = alterButton;
}
};
}
};
// Create extinction marker visuals
self.extinctionMarkerVisuals = [];
self.updateExtinctionMarkers = function () {
// Remove existing visuals
for (var i = 0; i < self.extinctionMarkerVisuals.length; i++) {
if (self.extinctionMarkerVisuals[i].parent) {
self.extinctionMarkerVisuals[i].parent.removeChild(self.extinctionMarkerVisuals[i]);
}
}
self.extinctionMarkerVisuals = [];
// Create new visuals for current extinction markers
for (var i = 0; i < self.extinctionMarkers; i++) {
var marker = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
marker.tint = 0x000000; // Black for extinction markers
marker.x = (i - (self.extinctionMarkers - 1) / 2) * 20;
marker.y = 80;
self.addChild(marker);
self.extinctionMarkerVisuals.push(marker);
}
};
self.die = function () {
if (self.isInPlay) {
// Add creature to graveyard and draw event card based on creature tier
graveyard.push(self.creatureData);
// Draw event card only once when creature dies
addEventCardToDeck(self.creatureData.level);
// 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) {
// Prevent selecting creature/terrain cards if event cards are in hand
if (hasEventCardsInHand) {
//{1G_event}
return;
}
selectedCard = self;
draggedCard = self;
originalCardPosition = {
x: self.x,
y: self.y
};
showPossibleMoves(self);
}
};
return self;
});
var EventCard = Container.expand(function (eventData) {
var self = Container.call(this);
self.eventData = eventData || {
type: 'basic',
cardType: 'event',
level: 'Basic',
name: 'Event Card',
effect: 'environmental'
};
// Create event card visual
var eventAsset = self.attachAsset('eventCard', {
anchorX: 0.5,
anchorY: 0.5
});
// Add event text - placeholder "EVENT!!!" in red
var eventText = new Text2("EVENT!!!", {
size: 32,
fill: 0xFF0000
});
eventText.anchor.set(0.5, 0.5);
eventText.x = 0;
eventText.y = 0;
self.addChild(eventText);
// Add event level indicator
var levelText = new Text2(self.eventData.level, {
size: 16,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.x = 0;
levelText.y = -80;
self.addChild(levelText);
self.isInPlay = false;
self.move = function (x, y, obj) {
if (!self.isInPlay && playerHand.indexOf(self) !== -1) {
// Check if mouse is actually over this card
var cardBounds = 80;
if (Math.abs(x) < cardBounds && Math.abs(y) < 110) {
showCardInfo(self);
showPossibleMoves(self);
} else {
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);
// Event card selected - add to play queue if not already there
if (eventCardsToPlay.indexOf(self) === -1) {
//{2u_event}
eventCardsToPlay.push(self);
hasEventCardsInHand = true;
}
}
};
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',
level: '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.level === '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: 0x000000,
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);
// Advanced terrain cards already have the terrain type text, no need for duplicate text
// Add requirement text for advanced terrain cards
if (self.terrainData.level === '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) {
// Prevent selecting creature/terrain cards if event cards are in hand
if (hasEventCardsInHand) {
//{3H_event}
return;
}
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 = 3;
var mainDeck = [];
var basicTerrainPool = [];
var selectedCard = null;
var draggedCard = null;
var originalCardPosition = null;
var scryTokenActive = false; // Start without scry token
var scryCards = []; // Cards in scry mode
var scryMode = false; // Whether scry is active
var creaturesWentExtinct = false; // Track if creatures went extinct this round
var cardInfoText = null;
var hoveredCard = null;
var drawPhase = 'initial'; // 'initial', 'forced'
var cardsDrawnInPhase = [];
var discardPile = [];
var linkLines = []; // Visual link lines between carnivores and herbivores
var gamePhase = 'dawn'; // Track current game phase: 'dawn', 'draw', 'noon', 'dusk', 'night', 'end'
var turnNumber = 1;
var draggedLinkMarker = null;
var draggedLinkCarnivore = null;
var linkHighlights = [];
var eventCardsToPlay = [];
var mustPlayCard = null;
var basicEventDeck = [];
var advancedEventDeck = [];
var phaseTransitionTimer = null;
var scryDeniedNextRound = false;
var hasEventCardsInHand = false;
// 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 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 discardCountText = new Text2("Discard: 0", {
size: 36,
fill: 0xFFFFFF
});
discardCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(discardCountText);
discardCountText.x = 120;
discardCountText.y = 190;
var basicEventCountText = new Text2("Basic Events: 10", {
size: 28,
fill: 0x00FF00
});
basicEventCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(basicEventCountText);
basicEventCountText.x = 120;
basicEventCountText.y = 230;
var advancedEventCountText = new Text2("Advanced Events: 10", {
size: 28,
fill: 0xFF6600
});
advancedEventCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(advancedEventCountText);
advancedEventCountText.x = 120;
advancedEventCountText.y = 260;
var graveyardCountText = new Text2("Graveyard: 0", {
size: 28,
//{4f_graveyard}
fill: 0xFFFFFF
}); //{4g_graveyard}
graveyardCountText.anchor.set(0, 0);
LK.gui.topLeft.addChild(graveyardCountText);
graveyardCountText.x = 120;
graveyardCountText.y = 290;
var graveyard = [];
var scryText = new Text2("Scry", {
size: 32,
fill: 0xFFFFFF
});
scryText.anchor.set(0, 0);
scryText.tint = 0x888888; // Start gray/inactive
LK.gui.topRight.addChild(scryText);
scryText.x = -300;
scryText.y = 190;
// Add scry button functionality
scryText.down = function () {
if (scryTokenActive && !scryMode && mainDeck.length >= 3) {
activateScry();
}
};
// 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',
level: 'Basic',
subtype: 'water',
waterType: 'sea',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 3; i++) {
basicTerrainPool.push({
type: 'basic',
level: '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',
level: 'Basic',
subtype: 'land',
landType: 'mountain',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 5; i++) {
basicTerrainPool.push({
type: 'basic',
level: 'Basic',
subtype: 'land',
landType: 'hills',
climate: null // Will be assigned during setup
});
}
for (var i = 0; i < 5; i++) {
basicTerrainPool.push({
type: 'basic',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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',
level: '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 shuffleEventDeck(eventDeck) {
for (var i = eventDeck.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = eventDeck[i];
eventDeck[i] = eventDeck[j];
eventDeck[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 + " | Level: " + creature.level + " " + 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.level + " " + (terrain.landType || terrain.waterType);
infoString += " | Level: " + terrain.level + " 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 hasValidPlacements(cards) {
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
if (card.creatureData) {
// Check creature placements
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && canPlaceCreatureOnTerrain(card, terrain)) {
return true;
}
}
}
} else if (card.terrainData) {
// Check terrain placements
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && canPlaceTerrainOnTerrain(card, terrain)) {
return true;
}
}
}
}
}
return false;
}
function discardCards(cards) {
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
// Remove from hand
var handIndex = playerHand.indexOf(card);
if (handIndex !== -1) {
playerHand.splice(handIndex, 1);
}
// Remove from game
if (card.parent) {
card.parent.removeChild(card);
}
// Extract raw card data and add to discard pile
var cardData = card.creatureData || card.terrainData || card.eventData;
discardPile.push(cardData);
}
updateHandPositions();
}
function drawCard() {
if (mainDeck.length === 0) {
endRound();
return null;
}
// Enforce maximum hand size of 3
if (playerHand.length >= maxHandSize) {
return null; // Hand is full
}
if (drawPhase === 'complete') {
return null; // Draw phase already complete
}
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);
}
if (drawPhase === 'initial') {
// Initial draw phase - draw up to 3 cards
playerHand.push(card);
cardsDrawnInPhase.push(card);
cardsDrawnThisTurn++;
if (cardsDrawnInPhase.length >= 3) {
// Check if any of the 3 cards have valid placements
if (!hasValidPlacements(cardsDrawnInPhase)) {
// Discard all 3 cards and enter forced draw phase
discardCards(cardsDrawnInPhase);
cardsDrawnInPhase = [];
drawPhase = 'forced';
// Continue drawing in forced phase
return drawCard();
} else {
// At least one card has valid placement, end draw phase
drawPhase = 'complete';
}
}
} else if (drawPhase === 'forced') {
// Forced draw phase - draw one card at a time until playable
if (hasValidPlacements([card])) {
// This card can be played, add to hand and must be played
playerHand.push(card);
drawPhase = 'complete';
// Mark this card as must play
card.mustPlay = true;
} else {
// Card cannot be played, discard it and draw another
discardCards([card]);
return drawCard();
}
} else {
return null; // Draw phase complete
}
// Position card in hand
updateHandPositions();
game.addChild(card);
// Update UI
deckCountText.setText("Deck: " + mainDeck.length);
LK.getSound('cardDraw').play();
return card;
}
function startDrawPhase() {
if (mainDeck.length === 0) {
endRound();
return;
}
drawPhase = 'initial';
cardsDrawnInPhase = [];
cardsDrawnThisTurn = 0;
// Draw initial 3 cards
for (var i = 0; i < 3; i++) {
if (mainDeck.length > 0) {
drawCard();
}
}
}
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.level !== 'Advanced') return false;
// Check if target is a basic terrain card
if (!basicTerrain || basicTerrain.level !== '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 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 - use basic terrain climate if advanced terrain is placed on basic
var terrainClimate = terrain.climate;
// If this is an advanced terrain on basic terrain, use the basic terrain's climate
if (terrainCard.basicTerrainUnderneath && terrainCard.basicTerrainUnderneath.terrainData) {
terrainClimate = terrainCard.basicTerrainUnderneath.terrainData.climate;
}
if (creature.climateRequirement && 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] === terrainClimate) {
climateMatches = true;
break;
}
}
} else {
// Single climate requirement
climateMatches = creature.climateRequirement === terrainClimate;
}
if (!climateMatches) return false;
}
// Advanced creatures can only be placed on advanced terrain
if (creature.level === 'Advanced') {
if (terrain.level !== 'Advanced') return false;
}
// Basic herbivores can only be placed on basic terrain (but advanced terrain counts as valid if it's on basic terrain)
if (creature.level === 'Basic' && creature.dietType === 'herbivore') {
// Allow placement on advanced terrain that's placed on basic terrain, or directly on basic terrain
if (terrain.level !== 'Basic' && !terrainCard.basicTerrainUnderneath) return false;
}
// Nothing can be placed on top of advanced carnivores
if (terrainCard.creatureStack.length > 0) {
//{9U_adv}
var topCreature = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
if (topCreature.creatureData.level === 'Advanced' && topCreature.creatureData.dietType === 'carnivore') {
return false; // Cannot place anything on advanced carnivores
} //{9U_adv2}
} //{9U_adv3}
// Basic carnivores can be placed on any terrain type (basic or advanced) - no restriction needed
// Check creature stacking rules
if (terrainCard.creatureStack.length > 0) {
var topCreatureInStack = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
// Basic creature stacking rules
if (creature.level === 'Basic') {
// Basic herbivore restrictions
if (creature.dietType === 'herbivore') {
if (topCreatureInStack.creatureData.level === 'Basic' && topCreatureInStack.creatureData.dietType === 'herbivore') {
return false; // Basic herbivores cannot be placed on stacks with other basic herbivores at top
}
if (topCreatureInStack.creatureData.level === 'Advanced' && topCreatureInStack.creatureData.dietType === 'herbivore') {
return false; // Basic herbivores cannot be placed on stacks with advanced herbivores at top
}
if (topCreatureInStack.creatureData.level === 'Basic' && topCreatureInStack.creatureData.dietType === 'carnivore') {
return false; // Basic herbivores cannot be placed on stacks with basic carnivores at top
}
}
// Basic carnivore restrictions
if (creature.dietType === 'carnivore') {
if (topCreatureInStack.creatureData.level === 'Basic' && topCreatureInStack.creatureData.dietType === 'carnivore') {
return false; // Basic carnivores cannot be placed on stacks with other basic carnivores at top
}
if (topCreatureInStack.creatureData.level === 'Advanced' && topCreatureInStack.creatureData.dietType === 'carnivore') {
return false; // Basic carnivores cannot be placed on stacks with advanced carnivores at top
}
if (topCreatureInStack.creatureData.level === 'Advanced' && topCreatureInStack.creatureData.dietType === 'herbivore') {
return false; // Basic carnivores cannot be placed on stacks with advanced herbivores at top
}
}
}
// Advanced creature stacking rules
else if (creature.level === 'Advanced') {
// Advanced herbivores can only be placed on advanced terrain with basic herbivores at top
if (creature.dietType === 'herbivore') {
var validTarget = topCreatureInStack.creatureData && topCreatureInStack.creatureData.level === 'Basic' && topCreatureInStack.creatureData.dietType === 'herbivore';
if (!validTarget) return false;
}
// Advanced carnivores can be placed on stacks with basic carnivores OR advanced herbivores at top
else if (creature.dietType === 'carnivore') {
var validTarget = topCreatureInStack.creatureData && topCreatureInStack.creatureData.level === 'Basic' && topCreatureInStack.creatureData.dietType === 'carnivore' || topCreatureInStack.creatureData && topCreatureInStack.creatureData.level === 'Advanced' && topCreatureInStack.creatureData.dietType === 'herbivore';
if (!validTarget) return false;
}
}
} else {
// No creatures in stack - advanced creatures cannot be placed on empty terrain
if (creature.level === 'Advanced') {
return false; // Advanced creatures require existing creatures to stack on
}
}
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);
// When advancing terrain (creating advanced carnivore), remove all links from the old basic carnivore below
if (basicTerrainCard.creatureStack.length > 0) {
var oldTopCreature = basicTerrainCard.creatureStack[basicTerrainCard.creatureStack.length - 1];
if (oldTopCreature.creatureData.dietType === 'carnivore' && oldTopCreature.creatureData.level === 'Basic') {
// Remove all links from this basic carnivore before it evolves
for (var linkIdx = oldTopCreature.activeLinks.length - 1; linkIdx >= 0; linkIdx--) {
animateLinkFallOff(oldTopCreature, oldTopCreature.activeLinks[linkIdx].target);
} //{az_new}
} //{az_new2}
} //{az_new3}
// Check for invalid links when terrain changes (shouldn't affect creature types, but safety check)
if (advancedTerrainCard.creatureStack.length > 0) {
var topCreature = advancedTerrainCard.creatureStack[advancedTerrainCard.creatureStack.length - 1];
checkAndRemoveInvalidLinks(gridX, gridY, topCreature);
}
// 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 draw phase after successful placement
if (advancedTerrainCard.mustPlay) {
drawPhase = 'complete';
}
// Check if this was the last event card or must play card
if (eventCardsToPlay.length > 0) {
// Remove this card from event cards to play
var eventIndex = eventCardsToPlay.indexOf(advancedTerrainCard);
if (eventIndex !== -1) {
eventCardsToPlay.splice(eventIndex, 1);
}
// If no more event cards to play, clear mustPlayCard and check for phase progression
if (eventCardsToPlay.length === 0) {
mustPlayCard = null;
// Update event cards tracking
hasEventCardsInHand = false;
// Discard remaining non-event cards and progress to dusk
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
if (!playerHand[i].eventData) {
remainingCards.push(playerHand[i]);
}
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
}
} else if (advancedTerrainCard.mustPlay || mustPlayCard === advancedTerrainCard) {
// This was a must play card, clear it and progress to dusk
mustPlayCard = null;
// Discard remaining cards
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
remainingCards.push(playerHand[i]);
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
} else {
// Regular card played, discard remaining cards and progress to dusk
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
remainingCards.push(playerHand[i]);
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
}
// Reset turn
cardsDrawnThisTurn = 0;
LK.getSound('cardPlace').play();
// Show adjust links button after card placement
showAdjustLinksButton();
return true;
}
function checkAndRemoveInvalidLinks(gridX, gridY, newTopCreature) {
// Check all links that involve creatures at this position
var invalidLinks = [];
// Check links FROM carnivores at this position
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var creature = terrain.creatureStack[i];
if (creature.creatureData.dietType === 'carnivore') {
// Check each active link from this carnivore
for (var j = creature.activeLinks.length - 1; j >= 0; j--) {
var link = creature.activeLinks[j];
var targetHerbivore = link.target;
// If the new top creature is a carnivore and the target is now covered, link becomes invalid
if (newTopCreature && newTopCreature.creatureData.dietType === 'carnivore' && targetHerbivore.gridX === gridX && targetHerbivore.gridY === gridY) {
invalidLinks.push({
carnivore: creature,
herbivore: targetHerbivore
});
}
}
}
}
}
// Check links TO herbivores at this position (if new creature is carnivore, herbivores below become invalid targets)
if (newTopCreature && newTopCreature.creatureData.dietType === 'carnivore') {
// Find all links pointing to herbivores at this position that are now covered
for (var linkIndex = linkLines.length - 1; linkIndex >= 0; linkIndex--) {
var linkLine = linkLines[linkIndex];
if (linkLine.herbivore.gridX === gridX && linkLine.herbivore.gridY === gridY) {
// This herbivore is now covered by a carnivore, so links to it are invalid
invalidLinks.push({
carnivore: linkLine.carnivore,
herbivore: linkLine.herbivore
});
}
}
}
// Remove invalid links with fall-off animation
for (var k = 0; k < invalidLinks.length; k++) {
var invalidLink = invalidLinks[k];
animateLinkFallOff(invalidLink.carnivore, invalidLink.herbivore);
}
}
function animateLinkFallOff(carnivore, herbivore) {
// Find the link line to animate
var linkLineToRemove = null;
for (var i = 0; i < linkLines.length; i++) {
if (linkLines[i].carnivore === carnivore && linkLines[i].herbivore === herbivore) {
linkLineToRemove = linkLines[i];
break;
}
}
if (linkLineToRemove) {
// Animate the link falling off
tween(linkLineToRemove, {
alpha: 0,
scaleY: linkLineToRemove.scaleY * 0.1,
y: linkLineToRemove.y + 50
}, {
duration: 500,
onFinish: function onFinish() {
// Remove the link after animation
removeLink(carnivore, herbivore);
}
});
} else {
// No visual line found, just remove the link immediately
removeLink(carnivore, herbivore);
}
}
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);
}
// Check for invalid links before placing the new creature
checkAndRemoveInvalidLinks(gridX, gridY, creatureCard);
// If placing an advanced herbivore on top of a basic herbivore, remove all links to the basic herbivore below
if (creatureCard.creatureData.dietType === 'herbivore' && creatureCard.creatureData.level === 'Advanced') {
if (terrainCard.creatureStack.length > 0) {
var basicHerbivoreBelow = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
if (basicHerbivoreBelow.creatureData.dietType === 'herbivore' && basicHerbivoreBelow.creatureData.level === 'Basic') {
// Remove all links pointing to the basic herbivore below
for (var linkIdx = linkLines.length - 1; linkIdx >= 0; linkIdx--) {
if (linkLines[linkIdx].herbivore === basicHerbivoreBelow) {
animateLinkFallOff(linkLines[linkIdx].carnivore, basicHerbivoreBelow);
}
}
}
}
}
// 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];
// Clear extinction markers when another creature is placed on top
existingCreature.extinctionMarkers = 0;
existingCreature.updateExtinctionMarkers();
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);
// Create link markers for carnivores when placed
creatureCard.createLinkMarkers();
// Update hand positions
updateHandPositions();
// Clear selection
selectedCard = null;
draggedCard = null;
// Reset draw phase after successful placement
if (creatureCard.mustPlay) {
drawPhase = 'complete';
}
// Check if this was the last event card or must play card
if (eventCardsToPlay.length > 0) {
// Remove this card from event cards to play
var eventIndex = eventCardsToPlay.indexOf(creatureCard);
if (eventIndex !== -1) {
eventCardsToPlay.splice(eventIndex, 1);
}
// If no more event cards to play, clear mustPlayCard and check for phase progression
if (eventCardsToPlay.length === 0) {
mustPlayCard = null;
// Update event cards tracking
hasEventCardsInHand = false;
// Discard remaining non-event cards and progress to dusk
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
if (!playerHand[i].eventData) {
remainingCards.push(playerHand[i]);
}
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
}
} else if (creatureCard.mustPlay || mustPlayCard === creatureCard) {
// This was a must play card, clear it and progress to dusk
mustPlayCard = null;
// Discard remaining cards
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
remainingCards.push(playerHand[i]);
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
} else {
// Regular card played, discard remaining cards and progress to dusk
var remainingCards = [];
for (var i = 0; i < playerHand.length; i++) {
remainingCards.push(playerHand[i]);
}
if (remainingCards.length > 0) {
discardCards(remainingCards);
}
gamePhase = 'dusk';
}
// Reset turn
cardsDrawnThisTurn = 0;
LK.getSound('cardPlace').play();
// Show adjust links button after card placement
showAdjustLinksButton();
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 createLink(carnivore, herbivore) {
if (!carnivore || !herbivore) return false;
if (carnivore.creatureData.dietType !== 'carnivore') return false;
if (herbivore.creatureData.dietType !== 'herbivore') return false;
// Check if carnivore and herbivore are adjacent
if (!areCreaturesAdjacent(carnivore, herbivore)) return false;
// Additional check: ensure we're linking to the top creature only
var herbivoreGridX = herbivore.gridX;
var herbivoreGridY = herbivore.gridY;
var terrain = planetBoard[herbivoreGridY][herbivoreGridX];
if (terrain && terrain.creatureStack.length > 0) {
var topCreature = terrain.creatureStack[terrain.creatureStack.length - 1];
if (topCreature !== herbivore) {
return false; // Can only link to top creature in stack
}
}
// For basic carnivores, check if already linked to this herbivore
if (carnivore.creatureData.level === 'Basic') {
for (var i = 0; i < carnivore.activeLinks.length; i++) {
if (carnivore.activeLinks[i].target === herbivore) {
return false; // Basic carnivores can only have one link per herbivore
}
}
}
// Find an unlinked marker
var availableMarker = null;
for (var i = 0; i < carnivore.linkMarkers.length; i++) {
if (!carnivore.linkMarkers[i].isLinked) {
availableMarker = carnivore.linkMarkers[i];
break;
}
}
if (!availableMarker) return false; // No available link markers
// Create the link
availableMarker.isLinked = true;
availableMarker.targetHerbivore = herbivore;
availableMarker.tint = 0x00FF00; // Green for linked
// Add to active links
carnivore.activeLinks.push({
marker: availableMarker,
target: herbivore
});
// Create visual link line
createLinkLine(carnivore, herbivore);
return true;
}
function removeLink(carnivore, herbivore) {
if (!carnivore || !herbivore) return false;
// Find and remove the link
for (var i = carnivore.activeLinks.length - 1; i >= 0; i--) {
var link = carnivore.activeLinks[i];
if (link.target === herbivore) {
link.marker.isLinked = false;
link.marker.targetHerbivore = null;
link.marker.tint = 0xFF0000; // Red for unlinked
carnivore.activeLinks.splice(i, 1);
// Remove visual link line
removeLinkLine(carnivore, herbivore);
return true;
}
}
return false;
}
function areCreaturesAdjacent(creature1, creature2) {
if (!creature1.isInPlay || !creature2.isInPlay) return false;
var dx = Math.abs(creature1.gridX - creature2.gridX);
var dy = Math.abs(creature1.gridY - creature2.gridY);
// Adjacent means exactly one grid space away (not diagonal)
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function createLinkLine(carnivore, herbivore) {
// Remove existing line between these creatures if any
removeLinkLine(carnivore, herbivore);
// Create visual arrow line using a thicker rectangle
var line = LK.getAsset('terrainLandFlat', {
anchorX: 0,
anchorY: 0.5
});
line.tint = 0xFFFF00; // Yellow link line
line.alpha = 0.8;
// Position arrow starting from slightly inside carnivore border towards herbivore
var dx = herbivore.x - carnivore.x;
var dy = herbivore.y - carnivore.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
// Calculate starting position slightly inside carnivore border (about 20px inward)
var startOffsetX = Math.cos(angle) * 20;
var startOffsetY = Math.sin(angle) * 20;
var startX = carnivore.x + startOffsetX;
var startY = carnivore.y + startOffsetY;
// Calculate gap distance (reduce total distance by both card radii plus small gap)
var gapDistance = distance - 100; // 50px from each card edge creates gap
line.x = startX;
line.y = startY;
line.rotation = angle;
line.scaleX = gapDistance / 252; // Scale based on gap distance
line.scaleY = 0.08; // Thicker line (8% of original height)
line.carnivore = carnivore;
line.herbivore = herbivore;
game.addChild(line);
linkLines.push(line);
// Check if there are duplicate links from this carnivore to this herbivore
var duplicateCount = 0;
for (var dupIdx = 0; dupIdx < linkLines.length; dupIdx++) {
if (linkLines[dupIdx].carnivore === carnivore && linkLines[dupIdx].herbivore === herbivore) {
duplicateCount++;
} //{e9_dup}
} //{e9_dup2}
// If there are 2 or more links, add X2 indicator
if (duplicateCount >= 2) {
var x2Indicator = new Text2("X2", {
size: 24,
//{e9_x2}
fill: 0xFF0000
}); //{e9_x2b}
x2Indicator.anchor.set(0.5, 0.5);
x2Indicator.x = line.x + Math.cos(line.rotation) * (line.scaleX * 252 / 2);
x2Indicator.y = line.y + Math.sin(line.rotation) * (line.scaleX * 252 / 2);
game.addChild(x2Indicator);
line.x2Indicator = x2Indicator;
} //{e9_x2c}
}
function removeLinkLine(carnivore, herbivore) {
for (var i = linkLines.length - 1; i >= 0; i--) {
var line = linkLines[i];
if (line.carnivore === carnivore && line.herbivore === herbivore) {
if (line.parent) {
line.parent.removeChild(line);
}
// Remove X2 indicator if it exists
if (line.x2Indicator && line.x2Indicator.parent) {
line.x2Indicator.parent.removeChild(line.x2Indicator);
} //{ea_x2}
linkLines.splice(i, 1);
break;
}
}
}
function highlightValidLinkTargets(carnivore) {
// Clear existing highlights
clearLinkHighlights();
if (!carnivore || carnivore.creatureData.dietType !== 'carnivore') return;
// Find adjacent herbivores that can be linked to
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
// Only consider the top creature in each stack
var topCreature = terrain.creatureStack[terrain.creatureStack.length - 1];
if (topCreature.creatureData.dietType === 'herbivore' && areCreaturesAdjacent(carnivore, topCreature)) {
var canCreateLink = false;
// For basic carnivores, check if already linked to this herbivore
if (carnivore.creatureData.level === 'Basic') {
var hasExistingLink = false;
for (var j = 0; j < carnivore.activeLinks.length; j++) {
if (carnivore.activeLinks[j].target === topCreature) {
hasExistingLink = true;
break;
}
}
if (!hasExistingLink) {
canCreateLink = true;
}
} else {
// Advanced carnivores can always create links if they have available markers
var hasAvailableMarker = false;
for (var k = 0; k < carnivore.linkMarkers.length; k++) {
if (!carnivore.linkMarkers[k].isLinked) {
hasAvailableMarker = true;
break;
}
}
if (hasAvailableMarker) {
canCreateLink = true;
}
}
if (canCreateLink) {
// Create highlight around this herbivore
var highlight = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5
});
highlight.tint = 0x00FF00; // Green highlight
highlight.alpha = 0.3;
highlight.scaleX = topCreature.scaleX * 1.2;
highlight.scaleY = topCreature.scaleY * 1.2;
highlight.x = topCreature.x;
highlight.y = topCreature.y;
highlight.targetCreature = topCreature;
game.addChild(highlight);
linkHighlights.push(highlight);
}
}
}
}
}
}
function clearLinkHighlights() {
for (var i = 0; i < linkHighlights.length; i++) {
if (linkHighlights[i].parent) {
linkHighlights[i].parent.removeChild(linkHighlights[i]);
}
}
linkHighlights = [];
}
function updateLinkMarkerStates() {
// Update all link markers based on their state and game phase
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var creature = terrain.creatureStack[i];
if (creature.creatureData.dietType === 'carnivore') {
// Check if this carnivore will become extinct (insufficient links)
var willBecomeExtinct = creature.activeLinks.length < creature.linkRequirement;
for (var j = 0; j < creature.linkMarkers.length; j++) {
var marker = creature.linkMarkers[j];
if (gamePhase === 'dusk' && linkAdjustmentMode) {
// During dusk phase with adjustment mode active, make markers interactive
if (marker.isLinked) {
marker.tint = 0x00FF00; // Green for linked
// Make linked markers draggable to remove links
marker.down = function (x, y, obj) {
draggedLinkMarker = this;
draggedLinkCarnivore = this.carnivore;
};
} else {
// Check if this marker can be legally linked
var hasValidTargets = false;
var adjacentTerrains = getAdjacentTerrains(gridX, gridY);
for (var k = 0; k < adjacentTerrains.length; k++) {
var adjTerrain = adjacentTerrains[k];
if (adjTerrain && adjTerrain.creatureStack.length > 0) {
// Only consider the top creature in each adjacent stack
var topHerbivore = adjTerrain.creatureStack[adjTerrain.creatureStack.length - 1];
if (topHerbivore.creatureData.dietType === 'herbivore') {
// For basic carnivores, check if already linked to this herbivore
if (creature.creatureData.level === 'Basic') {
var hasExistingLink = false;
for (var m = 0; m < creature.activeLinks.length; m++) {
if (creature.activeLinks[m].target === topHerbivore) {
hasExistingLink = true;
break;
}
}
if (!hasExistingLink) {
hasValidTargets = true;
break;
}
} else {
// Advanced carnivores can always link if they have available markers
hasValidTargets = true;
break;
}
}
}
if (hasValidTargets) break;
}
if (hasValidTargets) {
marker.tint = 0xFFFF00; // Yellow for linkable
// Make unlinked but valid markers draggable
marker.down = function (x, y, obj) {
draggedLinkMarker = this;
draggedLinkCarnivore = this.carnivore;
};
} else {
marker.tint = 0x808080; // Gray for non-linkable
marker.down = function (x, y, obj) {
// Do nothing for non-linkable markers
};
// Add N/A text
if (!marker.naText) {
marker.naText = new Text2("N/A", {
size: 8,
fill: 0xFFFFFF
});
marker.naText.anchor.set(0.5, 0.5);
marker.naText.x = 0;
marker.naText.y = 0;
marker.addChild(marker.naText);
}
}
}
} else {
// Outside dusk phase or not in adjustment mode, markers are not interactive
if (marker.isLinked) {
marker.tint = 0x00FF00; // Green for linked
} else {
marker.tint = 0xFF0000; // Red for unlinked
}
// Remove down handler and N/A text if present
marker.down = function (x, y, obj) {};
if (marker.naText && marker.naText.parent) {
marker.naText.parent.removeChild(marker.naText);
marker.naText = null;
}
}
// Flash markers if carnivore will become extinct
if (willBecomeExtinct) {
// Stop any existing flash animation
tween.stop(marker, {
alpha: true
});
// Start flashing animation
tween(marker, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(marker, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart the flash cycle if still extinct
if (creature.activeLinks.length < creature.linkRequirement) {
updateLinkMarkerStates();
}
}
});
}
});
} else {
// Stop flashing if no longer about to become extinct
tween.stop(marker, {
alpha: true
});
marker.alpha = 1;
}
}
}
}
}
}
}
}
function autoCreateLinks() {
// Auto-create mandatory links for carnivores
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var carnivore = terrain.creatureStack[i];
if (carnivore.creatureData.dietType === 'carnivore') {
// Try to create links for this carnivore
while (carnivore.activeLinks.length < carnivore.linkRequirement) {
var linkCreated = false;
// Find adjacent herbivores
var adjacentTerrains = getAdjacentTerrains(gridX, gridY);
for (var j = 0; j < adjacentTerrains.length && !linkCreated; j++) {
var adjTerrain = adjacentTerrains[j];
if (adjTerrain && adjTerrain.creatureStack.length > 0) {
// Only consider the top creature in each adjacent stack
var topHerbivore = adjTerrain.creatureStack[adjTerrain.creatureStack.length - 1];
if (topHerbivore.creatureData.dietType === 'herbivore') {
// Check if we can create a link
var hasExistingLink = false;
for (var l = 0; l < carnivore.activeLinks.length; l++) {
if (carnivore.activeLinks[l].target === topHerbivore) {
hasExistingLink = true;
break;
}
}
if (!hasExistingLink && createLink(carnivore, topHerbivore)) {
linkCreated = true;
}
}
}
}
if (!linkCreated) break; // No more links possible
}
}
}
}
}
}
}
function processLinkConsequences() {
// Process extinction markers for herbivores based on excess links
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var herbivore = terrain.creatureStack[i];
if (herbivore.creatureData.dietType === 'herbivore') {
// Count links pointing to this herbivore
var linksToThisHerbivore = 0;
for (var j = 0; j < linkLines.length; j++) {
if (linkLines[j].herbivore === herbivore) {
linksToThisHerbivore++;
}
}
// Add extinction marker ONLY if this is a NEW excess link situation
var excessLinks = linksToThisHerbivore - herbivore.safeLinks;
if (excessLinks > 0) {
// Only add marker if this herbivore wasn't already penalized for excess links
if (!herbivore.penalizedForExcessLinksThisTurn) {
herbivore.extinctionMarkers += 1; // Add only 1 marker per turn, not per excess link
herbivore.updateExtinctionMarkers();
herbivore.penalizedForExcessLinksThisTurn = true; // Mark that we penalized this turn
} //{gq_new}
} else {
// Reset the penalty flag when excess links are resolved
herbivore.penalizedForExcessLinksThisTurn = false;
}
}
}
}
}
}
}
function processNightPhase() {
// Show visual cue that night phase is processing
showPhaseTransition("Night Phase - Checking links...", function () {
// Add extinction markers to carnivores without sufficient links
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var herbivore = terrain.creatureStack[i];
if (herbivore.creatureData.dietType === 'herbivore') {
// Count links pointing to this herbivore
var linksToThisHerbivore = 0;
for (var j = 0; j < linkLines.length; j++) {
if (linkLines[j].herbivore === herbivore) {
linksToThisHerbivore++;
}
}
// Only add extinction marker if herbivore has excess links AND at least 1 link minimum
var excessLinks = linksToThisHerbivore - herbivore.safeLinks;
if (excessLinks > 0 && linksToThisHerbivore >= 1) {
// Check if herbivore already has extinction marker(s) - if so, don't add another one this turn
// The marker persists from previous turn, so having a marker = already penalized
if (herbivore.extinctionMarkers === 0) {
//{hb_new}
herbivore.extinctionMarkers += 1;
herbivore.updateExtinctionMarkers();
} //{hd_new}
}
} else if (herbivore.creatureData.dietType === 'carnivore') {
if (herbivore.activeLinks.length < herbivore.linkRequirement) {
herbivore.extinctionMarkers += 1; // Only 1 marker per turn regardless of shortage
herbivore.updateExtinctionMarkers();
}
}
}
}
}
}
// Auto-progress to end turn phase
gamePhase = 'end';
processEndTurnPhase();
});
}
function processExtinctions() {
// Remove creatures with 2 or more extinction markers
var extinctCreatures = [];
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = terrain.creatureStack.length - 1; i >= 0; i--) {
var creature = terrain.creatureStack[i];
if (creature.extinctionMarkers >= 2) {
extinctCreatures.push(creature);
terrain.creatureStack.splice(i, 1);
creaturesWentExtinct = true; // Mark that creatures went extinct this round
scryDeniedNextRound = true; // Deny scry for next round when extinction occurs
// Clear extinction markers when creature leaves play
creature.extinctionMarkers = 0;
creature.updateExtinctionMarkers();
// Remove all links involving this creature with fall-off animation
if (creature.creatureData.dietType === 'carnivore') {
for (var j = creature.activeLinks.length - 1; j >= 0; j--) {
animateLinkFallOff(creature, creature.activeLinks[j].target);
}
} else {
// Remove links from carnivores to this herbivore with fall-off animation
for (var j = linkLines.length - 1; j >= 0; j--) {
if (linkLines[j].herbivore === creature) {
animateLinkFallOff(linkLines[j].carnivore, creature);
}
}
}
creature.die();
}
}
}
}
}
}
function createEventDecks() {
// Create 10 basic event cards
basicEventDeck = [];
for (var i = 1; i <= 10; i++) {
basicEventDeck.push({
type: 'basic',
cardType: 'event',
level: 'Basic',
name: 'Basic Event ' + i,
effect: 'environmental'
});
}
// Create 10 advanced event cards
advancedEventDeck = [];
for (var i = 1; i <= 10; i++) {
advancedEventDeck.push({
type: 'advanced',
cardType: 'event',
level: 'Advanced',
name: 'Advanced Event ' + i,
effect: 'catastrophic'
});
}
}
function addEventCardToDeck(creatureLevel) {
var eventCard = null;
// Determine which deck to draw from based on creature level
if (creatureLevel === 'Advanced') {
if (advancedEventDeck.length > 0) {
eventCard = advancedEventDeck.pop();
} else if (basicEventDeck.length > 0) {
// Fallback to basic events if advanced is empty
eventCard = basicEventDeck.pop();
}
} else {
if (basicEventDeck.length > 0) {
eventCard = basicEventDeck.pop();
} else if (advancedEventDeck.length > 0) {
// Fallback to advanced events if basic is empty
eventCard = advancedEventDeck.pop();
}
}
// If event card was drawn, add to discard pile (only draw 1 card per death)
if (eventCard) {
discardPile.push(eventCard);
}
}
function processDawnPhase() {
// Check for ongoing effects and costs that trigger during Dawn
// Check scry status at beginning of round - award token ONLY if no creatures went extinct last round
if (currentRound > 1 && !scryDeniedNextRound) {
scryTokenActive = true;
}
// Reset extinction tracking for the new round
creaturesWentExtinct = false;
// Reset scry denial for the new round
scryDeniedNextRound = false;
// Show visual cue that dawn phase is processing
showPhaseTransition("Dawn Phase - Processing...", function () {
gamePhase = 'draw';
processDrawPhase(); // Auto-progress to draw phase
});
}
function processDrawPhase() {
// Draw exactly 3 cards, handling deck reshuffling as needed
var cardsToDrawTotal = 3;
var cardsDrawnThisPhase = 0;
var drawnCards = [];
var continueDrawing = true;
// Iterative loop that continues until we have 3 cards or can't draw more
while (continueDrawing && playerHand.length < cardsToDrawTotal) {
// Check if deck is empty, reshuffle if needed
if (mainDeck.length === 0) {
// Shuffle discard pile to become new deck
if (discardPile.length > 0) {
mainDeck = discardPile.slice();
discardPile = [];
shuffleDeck();
currentRound++;
roundText.setText("Round: " + currentRound);
deckCountText.setText("Deck: " + mainDeck.length);
// Check for end game condition after round increment
if (currentRound >= 6) {
// Game ends immediately
LK.showYouWin();
return;
}
// Check and trigger scry status at beginning of new round
// Only activate scry if no extinctions occurred last round
if (!scryDeniedNextRound) {
scryTokenActive = true;
} //{ht_scry}
creaturesWentExtinct = false;
scryDeniedNextRound = false;
} else {
// No cards available, stop drawing
continueDrawing = false;
}
}
// Draw one card if deck has cards
if (mainDeck.length > 0 && continueDrawing) {
var cardData = mainDeck.pop();
var card = createCardFromData(cardData);
drawnCards.push(card);
playerHand.push(card);
game.addChild(card);
// Update hand positions after each card is added
updateHandPositions();
cardsDrawnThisPhase++;
} else if (mainDeck.length === 0) {
// No more cards in deck and discard is empty or was just exhausted, stop drawing
continueDrawing = false;
}
}
// Update UI
deckCountText.setText("Deck: " + mainDeck.length);
discardCountText.setText("Discard: " + discardPile.length);
LK.getSound('cardDraw').play();
// Check if any cards have valid placements
if (!hasValidPlacements(drawnCards)) {
// Discard all cards in hand and continue drawing forced cards
discardCards(playerHand.slice());
// Clear drawn cards and continue loop
drawnCards = [];
// Continue loop to draw forced cards instead of recursive call
// Loop will continue iterating above since continueDrawing is true and playerHand.length < 3
} else {
// At least one card has valid placement, end draw phase
gamePhase = 'noon';
}
}
function createCardFromData(cardData) {
var card;
if (cardData.cardType === 'terrain') {
card = new TerrainCard(cardData);
card.scaleX = 0.6;
card.scaleY = 0.6;
} else if (cardData.cardType === 'event') {
// Create event card using EventCard class
card = new EventCard(cardData);
} else {
// Default to creature card
card = new CreatureCard(cardData);
}
return card;
}
function processNoonPhase() {
// Check for event cards in hand - they must be played first
eventCardsToPlay = [];
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i].eventData) {
eventCardsToPlay.push(playerHand[i]);
}
}
if (eventCardsToPlay.length > 0) {
// Player must play event cards first
mustPlayCard = eventCardsToPlay[0]; // Force first event card
return;
}
// Check if player has valid moves
var validCards = [];
for (var i = 0; i < playerHand.length; i++) {
if (hasValidPlacements([playerHand[i]])) {
validCards.push(playerHand[i]);
}
}
if (validCards.length === 0 && playerHand.length === 3) {
// Show no playable cards notification
showNoPlayableCardsNotification();
return;
}
// Player can choose which card to play (if multiple valid options)
if (validCards.length > 1) {
// Discard non-chosen cards (this would be handled by player interaction)
// For now, just continue - player will choose
}
}
function showPhaseTransition(message, callback) {
// Clear any existing transition timer
if (phaseTransitionTimer) {
LK.clearTimeout(phaseTransitionTimer);
}
// Create transition message
var transitionText = new Text2(message, {
size: 40,
fill: 0xFFFF00
});
transitionText.anchor.set(0.5, 0.5);
transitionText.x = 1024;
transitionText.y = 1366;
transitionText.alpha = 0;
game.addChild(transitionText);
// Fade in message
tween(transitionText, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
// Show message for 1 second, then fade out and execute callback
phaseTransitionTimer = LK.setTimeout(function () {
tween(transitionText, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (transitionText.parent) {
transitionText.parent.removeChild(transitionText);
}
if (callback) callback();
}
});
}, 1000);
}
});
}
function processDuskPhase() {
// Show visual cue that dusk phase is processing
showPhaseTransition("Dusk Phase - Arrange links...", function () {
// Update link marker states to make them interactive
updateLinkMarkerStates();
// Player now manually arranges links - no auto-progression
// Phase will advance when player clicks next phase button
});
}
function processEndTurnPhase() {
// Show visual cue that end turn phase is processing
showPhaseTransition("End Turn - Processing extinctions...", function () {
// Process extinctions
processExtinctions();
// Extinction markers persist - they are NOT cleared here
turnNumber++;
gamePhase = 'dawn'; // Start next turn
processDawnPhase(); // Auto-progress to next dawn phase
});
}
function endRound() {
currentRound++;
if (currentRound > maxRounds) {
// Game complete
LK.showYouWin();
return;
}
// Reshuffle deck
createInitialDeck();
// Reset turn counter and draw phase
cardsDrawnThisTurn = 0;
drawPhase = 'complete';
cardsDrawnInPhase = [];
// Update UI
roundText.setText("Round: " + currentRound);
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 () {
if (gamePhase === 'draw') {
processDrawPhase();
}
};
// Phase management buttons - redesigned for proper turn flow
var nextPhaseButton = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
nextPhaseButton.tint = 0x32CD32; // Green for next phase
var nextPhaseButtonText = new Text2("NEXT PHASE", {
size: 24,
fill: 0xFFFFFF
});
nextPhaseButtonText.anchor.set(0.5, 0.5);
nextPhaseButton.addChild(nextPhaseButtonText);
nextPhaseButton.x = 500;
nextPhaseButton.y = 2400;
game.addChild(nextPhaseButton);
nextPhaseButton.down = function () {
// Exit link adjustment mode if active
if (linkAdjustmentMode) {
deactivateLinkAdjustmentMode();
hideAdjustLinksButton();
}
if (gamePhase === 'dawn') {
processDawnPhase();
} else if (gamePhase === 'draw') {
processDrawPhase();
} else if (gamePhase === 'noon') {
// Check if player has event cards that must be played first
if (eventCardsToPlay.length > 0 || mustPlayCard) {
// Must play event cards or mandatory card first
return;
}
gamePhase = 'dusk';
processDuskPhase();
} else if (gamePhase === 'dusk') {
// Apply consequences for over-hunted herbivores
processLinkConsequences();
clearLinkHighlights();
gamePhase = 'night';
processNightPhase();
} else if (gamePhase === 'night') {
processNightPhase();
} else if (gamePhase === 'end') {
processEndTurnPhase();
}
};
// Phase indicator
var phaseText = new Text2("Phase: NOON", {
size: 32,
fill: 0xFFFFFF
});
phaseText.anchor.set(0.5, 0);
phaseText.x = 1024;
phaseText.y = 2550;
game.addChild(phaseText);
// Link adjustment system variables
var linkAdjustmentMode = false;
var adjustLinkButton = null;
var cardsWithAdjustableLinks = [];
var currentlyAdjustingCard = null;
function createAdjustLinksButton() {
if (adjustLinkButton && adjustLinkButton.parent) {
adjustLinkButton.parent.removeChild(adjustLinkButton);
}
adjustLinkButton = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.8
});
adjustLinkButton.tint = 0xFFD700; // Gold for adjust links
var adjustButtonText = new Text2("Adjust Links?", {
size: 20,
fill: 0x000000
});
adjustButtonText.anchor.set(0.5, 0.5);
adjustLinkButton.addChild(adjustButtonText);
adjustLinkButton.x = 250; // Left of next phase button
adjustLinkButton.y = 2400;
game.addChild(adjustLinkButton);
adjustLinkButton.down = function () {
activateLinkAdjustmentMode();
};
}
function showAdjustLinksButton() {
createAdjustLinksButton();
}
function hideAdjustLinksButton() {
if (adjustLinkButton && adjustLinkButton.parent) {
adjustLinkButton.parent.removeChild(adjustLinkButton);
adjustLinkButton = null;
}
}
function activateLinkAdjustmentMode() {
linkAdjustmentMode = true;
currentlyAdjustingCard = null;
cardsWithAdjustableLinks = [];
// Find all carnivores on the board
for (var gridY = 0; gridY < planetHeight; gridY++) {
for (var gridX = 0; gridX < planetWidth; gridX++) {
var terrain = planetBoard[gridY][gridX];
if (terrain && terrain.creatureStack.length > 0) {
for (var i = 0; i < terrain.creatureStack.length; i++) {
var creature = terrain.creatureStack[i];
if (creature.creatureData.dietType === 'carnivore') {
cardsWithAdjustableLinks.push(creature);
}
}
}
}
}
// Create and display personal "Alter Links" buttons on all carnivore cards
for (var i = 0; i < cardsWithAdjustableLinks.length; i++) {
var card = cardsWithAdjustableLinks[i];
card.createPersonalAlterLinksButton();
}
}
function deactivateLinkAdjustmentMode() {
linkAdjustmentMode = false;
// Remove personal alter buttons from all cards and restore card colors
for (var i = 0; i < cardsWithAdjustableLinks.length; i++) {
var card = cardsWithAdjustableLinks[i];
card.tint = 0xFFFFFF; // Return to normal color
// Remove personal alter button
if (card.personalAlterButton && card.personalAlterButton.parent) {
card.personalAlterButton.parent.removeChild(card.personalAlterButton);
card.personalAlterButton = null;
}
}
cardsWithAdjustableLinks = [];
currentlyAdjustingCard = null;
}
// Handle card dragging and link highlighting on hold
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 if (draggedLinkMarker && gamePhase === 'dusk' && linkAdjustmentMode) {
// Handle link marker dragging - highlight valid targets only while holding
var carnivore = draggedLinkMarker.carnivore;
highlightValidLinkTargets(carnivore);
// Create temporary visual line from carnivore to cursor
// Remove any existing temp line
if (game.tempLinkLine && game.tempLinkLine.parent) {
game.tempLinkLine.parent.removeChild(game.tempLinkLine);
}
// Create new temp line
var tempLine = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.01,
scaleY: 1
});
tempLine.tint = 0xFFFF00; // Yellow temp line
tempLine.alpha = 0.5;
var dx = x - carnivore.x;
var dy = y - carnivore.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
tempLine.x = carnivore.x + dx / 2;
tempLine.y = carnivore.y + dy / 2;
tempLine.rotation = angle;
tempLine.scaleY = distance / 336;
game.addChild(tempLine);
game.tempLinkLine = tempLine;
} 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;
} else if (draggedLinkMarker && draggedLinkCarnivore && gamePhase === 'dusk' && linkAdjustmentMode) {
// Handle link marker release with new system
var linkCreated = false;
// Remove temporary line
if (game.tempLinkLine && game.tempLinkLine.parent) {
game.tempLinkLine.parent.removeChild(game.tempLinkLine);
game.tempLinkLine = null;
}
// Check if released over a valid herbivore
for (var i = 0; i < linkHighlights.length; i++) {
var highlight = linkHighlights[i];
if (highlight.targetCreature) {
var creature = highlight.targetCreature;
var distance = Math.sqrt(Math.pow(creature.x - x, 2) + Math.pow(creature.y - y, 2));
if (distance < 100) {
// Within range of the herbivore
// Create the link
if (createLink(draggedLinkCarnivore, creature)) {
linkCreated = true;
break;
}
}
}
}
// Clear highlights and reset drag state
clearLinkHighlights();
draggedLinkMarker = null;
draggedLinkCarnivore = null;
}
};
function activateScry() {
if (!scryTokenActive || scryMode || mainDeck.length < 3) return;
scryMode = true;
scryCards = [];
// Take top 3 cards from deck
for (var i = 0; i < 3; i++) {
var cardData = mainDeck.pop();
var card = createCardFromData(cardData);
scryCards.push(card);
playerHand.push(card);
game.addChild(card);
}
updateHandPositions();
// Show return cards button
showReturnCardsButton();
}
function showReturnCardsButton() {
var returnButton = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8
});
returnButton.tint = 0x8A2BE2; // Purple for scry actions
var returnButtonText = new Text2("READY TO RETURN CARDS", {
size: 20,
fill: 0xFFFFFF
});
returnButtonText.anchor.set(0.5, 0.5);
returnButton.addChild(returnButtonText);
returnButton.x = 1024;
returnButton.y = 2000;
game.addChild(returnButton);
returnButton.down = function () {
processScryReturn();
if (returnButton.parent) {
returnButton.parent.removeChild(returnButton);
}
};
}
function processScryReturn() {
// For each scry card in hand, player chooses where to return it
// For now, we'll implement a simple system - return to top of deck
for (var i = 0; i < scryCards.length; i++) {
var card = scryCards[i];
var handIndex = playerHand.indexOf(card);
if (handIndex !== -1) {
playerHand.splice(handIndex, 1);
if (card.parent) {
card.parent.removeChild(card);
}
// Return to top of deck (could be enhanced to let player choose top/bottom/discard)
var cardData = card.creatureData || card.terrainData || card.eventData;
// Create clean copy without event data to prevent hand state corruption
var cleanCardData = {};
cleanCardData.type = cardData.type;
cleanCardData.level = cardData.level;
cleanCardData.cardType = cardData.cardType;
if (cardData.dietType) cleanCardData.dietType = cardData.dietType;
if (cardData.name) cleanCardData.name = cardData.name;
if (cardData.terrainRequirement) cleanCardData.terrainRequirement = cardData.terrainRequirement;
if (cardData.climateRequirement) cleanCardData.climateRequirement = cardData.climateRequirement;
if (cardData.subtype) cleanCardData.subtype = cardData.subtype;
if (cardData.landType) cleanCardData.landType = cardData.landType;
if (cardData.waterType) cleanCardData.waterType = cardData.waterType;
if (cardData.climate) cleanCardData.climate = cardData.climate;
mainDeck.push(cleanCardData);
}
}
// Clear scry state
scryCards = [];
scryMode = false;
scryTokenActive = false; // Scry token consumed
updateHandPositions();
}
function showNoPlayableCardsNotification() {
// Create notification box
var notificationBox = LK.getAsset('terrainLandFlat', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.5
});
notificationBox.tint = 0x8B0000; // Dark red background
notificationBox.alpha = 0.9;
notificationBox.x = 1024;
notificationBox.y = 1366;
game.addChild(notificationBox);
// Create notification text
var notificationText = new Text2("No playable cards! Click to continue", {
size: 36,
fill: 0xFFFFFF
});
notificationText.anchor.set(0.5, 0.5);
notificationText.x = 0;
notificationText.y = 0;
notificationBox.addChild(notificationText);
// Make the box clickable
notificationBox.down = function () {
// Remove the notification
if (notificationBox.parent) {
notificationBox.parent.removeChild(notificationBox);
}
// Discard all cards in hand
discardCards(playerHand.slice());
// Start draw 1, discard 1 cycle
processDrawPhase();
};
// Store reference for cleanup
game.noPlayableNotification = notificationBox;
}
// Initialize terrain-based game
createBasicTerrainPool();
setupPlanet();
createInitialDeck();
createEventDecks();
// Shuffle event decks on game start
shuffleEventDeck(basicEventDeck);
shuffleEventDeck(advancedEventDeck);
// Game starts with turn 1, round 1, in Dawn phase
currentRound = 1;
turnNumber = 1;
gamePhase = 'dawn';
roundText.setText("Round: " + currentRound);
game.update = function () {
// Update UI elements
deckCountText.setText("Deck: " + mainDeck.length);
discardCountText.setText("Discard: " + discardPile.length);
basicEventCountText.setText("Basic Events: " + basicEventDeck.length);
advancedEventCountText.setText("Advanced Events: " + advancedEventDeck.length);
graveyardCountText.setText("Graveyard: " + graveyard.length);
// Update scry token display
if (scryTokenActive) {
scryText.setText("Scry ✓");
scryText.tint = 0x00FF00; // Green when available
} else if (scryMode) {
scryText.setText("Scry (Active)");
scryText.tint = 0xFFFF00; // Yellow when in use
} else {
scryText.setText("Scry");
scryText.tint = 0x888888; // Gray when unavailable
}
// Update phase indicator
phaseText.setText("Phase: " + gamePhase.toUpperCase() + " | Turn: " + turnNumber);
// Check if event cards have been removed from hand
hasEventCardsInHand = false;
for (var i = 0; i < playerHand.length; i++) {
//{lV_event1}
if (playerHand[i].eventData) {
hasEventCardsInHand = true;
break;
}
}
// Update next phase button appearance based on event cards
if (gamePhase === 'noon') {
//{lV_event2}
if (hasEventCardsInHand) {
nextPhaseButton.tint = 0xFF0000; // Red when events must be played
nextPhaseButtonText.setText("Play events 1st!");
} else {
nextPhaseButton.tint = 0x32CD32; // Green when normal phase progression
nextPhaseButtonText.setText("NEXT PHASE");
}
}
// Update link marker states during dusk phase
if (gamePhase === 'dusk') {
updateLinkMarkerStates();
}
};
function resetCardLinks(carnivore) {
if (!carnivore || carnivore.creatureData.dietType !== 'carnivore') return;
// Remove all active links from this carnivore with fall-off animation
for (var i = carnivore.activeLinks.length - 1; i >= 0; i--) {
var link = carnivore.activeLinks[i];
animateLinkFallOff(carnivore, link.target);
}
// Reset all link markers to unlinked state
for (var j = 0; j < carnivore.linkMarkers.length; j++) {
var marker = carnivore.linkMarkers[j];
marker.isLinked = false;
marker.targetHerbivore = null;
marker.tint = 0xFF0000; // Red for unlinked
}
// Clear active links array
carnivore.activeLinks = [];
} ===================================================================
--- original.js
+++ change.js
@@ -473,15 +473,15 @@
/****
* Game Code
****/
-// Game state variables
-// Terrain card assets - Land types with green strip
-// Terrain card assets - Water types with blue/grey strip
-// Climate indicator strips
-// Creature card assets
-// Event card asset
// 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;
@@ -1166,8 +1166,16 @@
if (creature.level === 'Basic' && creature.dietType === 'herbivore') {
// Allow placement on advanced terrain that's placed on basic terrain, or directly on basic terrain
if (terrain.level !== 'Basic' && !terrainCard.basicTerrainUnderneath) return false;
}
+ // Nothing can be placed on top of advanced carnivores
+ if (terrainCard.creatureStack.length > 0) {
+ //{9U_adv}
+ var topCreature = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
+ if (topCreature.creatureData.level === 'Advanced' && topCreature.creatureData.dietType === 'carnivore') {
+ return false; // Cannot place anything on advanced carnivores
+ } //{9U_adv2}
+ } //{9U_adv3}
// Basic carnivores can be placed on any terrain type (basic or advanced) - no restriction needed
// Check creature stacking rules
if (terrainCard.creatureStack.length > 0) {
var topCreatureInStack = terrainCard.creatureStack[terrainCard.creatureStack.length - 1];
@@ -1254,8 +1262,18 @@
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);
+ // When advancing terrain (creating advanced carnivore), remove all links from the old basic carnivore below
+ if (basicTerrainCard.creatureStack.length > 0) {
+ var oldTopCreature = basicTerrainCard.creatureStack[basicTerrainCard.creatureStack.length - 1];
+ if (oldTopCreature.creatureData.dietType === 'carnivore' && oldTopCreature.creatureData.level === 'Basic') {
+ // Remove all links from this basic carnivore before it evolves
+ for (var linkIdx = oldTopCreature.activeLinks.length - 1; linkIdx >= 0; linkIdx--) {
+ animateLinkFallOff(oldTopCreature, oldTopCreature.activeLinks[linkIdx].target);
+ } //{az_new}
+ } //{az_new2}
+ } //{az_new3}
// Check for invalid links when terrain changes (shouldn't affect creature types, but safety check)
if (advancedTerrainCard.creatureStack.length > 0) {
var topCreature = advancedTerrainCard.creatureStack[advancedTerrainCard.creatureStack.length - 1];
checkAndRemoveInvalidLinks(gridX, gridY, topCreature);
@@ -1668,16 +1686,40 @@
line.carnivore = carnivore;
line.herbivore = herbivore;
game.addChild(line);
linkLines.push(line);
+ // Check if there are duplicate links from this carnivore to this herbivore
+ var duplicateCount = 0;
+ for (var dupIdx = 0; dupIdx < linkLines.length; dupIdx++) {
+ if (linkLines[dupIdx].carnivore === carnivore && linkLines[dupIdx].herbivore === herbivore) {
+ duplicateCount++;
+ } //{e9_dup}
+ } //{e9_dup2}
+ // If there are 2 or more links, add X2 indicator
+ if (duplicateCount >= 2) {
+ var x2Indicator = new Text2("X2", {
+ size: 24,
+ //{e9_x2}
+ fill: 0xFF0000
+ }); //{e9_x2b}
+ x2Indicator.anchor.set(0.5, 0.5);
+ x2Indicator.x = line.x + Math.cos(line.rotation) * (line.scaleX * 252 / 2);
+ x2Indicator.y = line.y + Math.sin(line.rotation) * (line.scaleX * 252 / 2);
+ game.addChild(x2Indicator);
+ line.x2Indicator = x2Indicator;
+ } //{e9_x2c}
}
function removeLinkLine(carnivore, herbivore) {
for (var i = linkLines.length - 1; i >= 0; i--) {
var line = linkLines[i];
if (line.carnivore === carnivore && line.herbivore === herbivore) {
if (line.parent) {
line.parent.removeChild(line);
}
+ // Remove X2 indicator if it exists
+ if (line.x2Indicator && line.x2Indicator.parent) {
+ line.x2Indicator.parent.removeChild(line.x2Indicator);
+ } //{ea_x2}
linkLines.splice(i, 1);
break;
}
}