/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ActionButton = Container.expand(function (actionType) {
var self = Container.call(this);
self.actionType = actionType;
var buttonGraphics = self.attachAsset(actionType, {
anchorX: 0.5,
anchorY: 0.5
});
// Action image is now visible without text overlay
self.down = function (x, y, obj) {
// Visual feedback - make button slightly darker when pressed
buttonGraphics.tint = 0x666666;
if (self.actionType === 'pickaxe') {
// Mining action - check for adjacent rocks
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
if (!rock.destroyed) {
var dx = rock.x - player.x;
var dy = rock.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 80) {
rock.hit();
break; // Mine only one rock per click
}
}
}
} else if (self.actionType === 'sword') {
// Attack action - check for adjacent slimes
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
if (!slime.destroyed) {
var dx = slime.x - player.x;
var dy = slime.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 80 && hasLineOfSight(player.x, player.y, slime.x, slime.y)) {
slime.takeDamage(34); // 3 hits to kill (100/3 = ~34 damage)
LK.getSound('attack').play();
break; // Attack only one slime per click
}
}
}
}
};
self.up = function (x, y, obj) {
// Reset button color when released
buttonGraphics.tint = 0xFFFFFF;
};
return self;
});
var DirectionalButton = Container.expand(function (direction) {
var self = Container.call(this);
self.direction = direction;
self.isPressed = false;
self.moveTimer = null;
var buttonGraphics = self.attachAsset('arrow' + direction.charAt(0).toUpperCase() + direction.slice(1), {
anchorX: 0.5,
anchorY: 0.5
});
// Method to move player in the button's direction
self.movePlayer = function () {
var moveDistance = 20; // Increased movement distance for faster speed
var newX = player.x;
var newY = player.y;
if (self.direction === 'Up') {
newY = player.y - moveDistance;
} else if (self.direction === 'Down') {
newY = player.y + moveDistance;
} else if (self.direction === 'Left') {
newX = player.x - moveDistance;
} else if (self.direction === 'Right') {
newX = player.x + moveDistance;
}
// Keep player within bounds
newX = Math.max(64, Math.min(1984, newX));
newY = Math.max(164, Math.min(2668, newY));
// Check collision before moving
if (!checkCollisionWithSolids(newX, newY, 15)) {
player.x = newX;
player.y = newY;
// Update player facing direction based on movement
if (self.direction === 'Left') {
player.setFacingDirection('left');
} else if (self.direction === 'Right') {
player.setFacingDirection('right');
} else if (self.direction === 'Up') {
player.setFacingDirection('up');
} else if (self.direction === 'Down') {
player.setFacingDirection('down');
}
}
};
// Arrow image is now visible without text overlay
self.down = function (x, y, obj) {
// Visual feedback - make button slightly darker when pressed
buttonGraphics.tint = 0x3A7BC8;
self.isPressed = true;
// Move immediately on press
self.movePlayer();
// Start continuous movement timer
self.moveTimer = LK.setInterval(function () {
if (self.isPressed) {
self.movePlayer();
}
}, 80); // Move every 80ms while pressed for faster speed
};
self.up = function (x, y, obj) {
// Reset button color when released
buttonGraphics.tint = 0xFFFFFF;
self.isPressed = false;
// Clear continuous movement timer
if (self.moveTimer) {
LK.clearInterval(self.moveTimer);
self.moveTimer = null;
}
};
return self;
});
var FloorTile = Container.expand(function () {
var self = Container.call(this);
var tileGraphics = self.attachAsset('floorTile', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
// Add subtle border to show grid lines
tileGraphics.alpha = 0.3;
return self;
});
var HealthItem = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
width: 32,
height: 32
});
self.collected = false;
self.healAmount = 25;
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
player.heal(self.healAmount);
// Remove from healthItems array
for (var i = healthItems.length - 1; i >= 0; i--) {
if (healthItems[i] === self) {
healthItems.splice(i, 1);
break;
}
}
game.removeChild(self);
};
return self;
});
var InventoryButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function (x, y, obj) {
buttonGraphics.tint = 0x666666;
// Toggle inventory interface
if (inventoryInterface.visible) {
inventoryInterface.visible = false;
} else {
inventoryInterface.visible = true;
updateInventoryInterface();
}
};
self.up = function (x, y, obj) {
buttonGraphics.tint = 0xFFFFFF;
};
return self;
});
var InventoryInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Inventory', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -180;
self.addChild(titleText);
self.itemSlots = [];
// Create grid of item slots (6x4 grid)
var slotSize = 80;
var slotSpacing = 90;
var startX = -225;
var startY = -120;
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 6; col++) {
var slotContainer = new Container();
// Slot background
var slotBg = LK.getAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5
});
slotContainer.addChild(slotBg);
slotContainer.x = startX + col * slotSpacing;
slotContainer.y = startY + row * slotSpacing;
self.addChild(slotContainer);
self.itemSlots.push(slotContainer);
}
}
self.visible = false;
return self;
});
var Ladder = Container.expand(function () {
var self = Container.call(this);
var ladderGraphics = self.attachAsset('ladder', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.used = false;
self.use = function () {
if (self.used) return;
self.used = true;
currentFloor++;
generateFloor();
};
return self;
});
var Merchant = Container.expand(function () {
var self = Container.call(this);
var npcGraphics = self.attachAsset('npc', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
self.down = function (x, y, obj) {
// Open merchant interface
if (merchantInterface) {
merchantInterface.visible = true;
merchantInterface.showPriceGuide(); // Show price guide by default
}
};
return self;
});
var MerchantInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 600
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5,
width: 820,
height: 620
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Merchant', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -280;
self.addChild(titleText);
// Price Guide Button
var priceGuideButton = new Container();
var priceGuideBg = priceGuideButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 60
});
var priceGuideText = new Text2('Price Guide', {
size: 24,
fill: 0xFFFFFF
});
priceGuideText.anchor.set(0.5, 0.5);
priceGuideButton.addChild(priceGuideText);
priceGuideButton.x = -150;
priceGuideButton.y = -200;
self.addChild(priceGuideButton);
// Sell Items Button
var sellButton = new Container();
var sellBg = sellButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 60
});
var sellText = new Text2('Sell Items', {
size: 24,
fill: 0xFFFFFF
});
sellText.anchor.set(0.5, 0.5);
sellButton.addChild(sellText);
sellButton.x = 150;
sellButton.y = -200;
self.addChild(sellButton);
// Content area for price guide or selling interface
self.contentArea = new Container();
self.contentArea.y = -100;
self.addChild(self.contentArea);
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
var closeText = new Text2('X', {
size: 30,
fill: 0xFF0000
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 350;
closeButton.y = -250;
self.addChild(closeButton);
// Button event handlers
priceGuideButton.down = function (x, y, obj) {
priceGuideBg.tint = 0x666666;
self.showPriceGuide();
};
priceGuideButton.up = function (x, y, obj) {
priceGuideBg.tint = 0xFFFFFF;
};
sellButton.down = function (x, y, obj) {
sellBg.tint = 0x666666;
self.showSellingInterface();
};
sellButton.up = function (x, y, obj) {
sellBg.tint = 0xFFFFFF;
};
closeButton.down = function (x, y, obj) {
closeBg.tint = 0x666666;
self.visible = false;
};
closeButton.up = function (x, y, obj) {
closeBg.tint = 0xFFFFFF;
};
// Show price guide
self.showPriceGuide = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var priceItems = [{
item: 'rock',
price: 1
}, {
item: 'coal',
price: 3
}, {
item: 'copper_ore',
price: 5
}, {
item: 'iron_ore',
price: 8
}, {
item: 'gold_ore',
price: 15
}, {
item: 'gem',
price: 25
}, {
item: 'slime',
price: 2
}];
var startY = 0;
for (var i = 0; i < priceItems.length; i++) {
var itemContainer = new Container();
// Item sprite
var itemSprite = LK.getAsset(priceItems[i].item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
itemSprite.x = -100;
itemContainer.addChild(itemSprite);
// Price text
var priceText = new Text2(priceItems[i].price + ' coins', {
size: 24,
fill: 0xFFFFFF
});
priceText.anchor.set(0, 0.5);
priceText.x = -50;
itemContainer.addChild(priceText);
itemContainer.y = startY + i * 50;
self.contentArea.addChild(itemContainer);
}
};
// Show selling interface
self.showSellingInterface = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var yPos = 0;
var hasItems = false;
for (var item in inventory) {
if (inventory[item] > 0) {
hasItems = true;
var sellItemContainer = new Container();
// Item sprite
var itemSprite = LK.getAsset(item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
itemSprite.x = -150;
sellItemContainer.addChild(itemSprite);
// Item info text
var itemValue = getItemValue(item);
var totalValue = inventory[item] * itemValue;
var infoText = new Text2(item + ' x' + inventory[item] + ' = ' + totalValue + ' coins', {
size: 20,
fill: 0xFFFFFF
});
infoText.anchor.set(0, 0.5);
infoText.x = -100;
sellItemContainer.addChild(infoText);
// Sell button for this item
var sellItemButton = new Container();
var sellItemBg = sellItemButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 40
});
var sellItemText = new Text2('Sell', {
size: 18,
fill: 0xFFFFFF
});
sellItemText.anchor.set(0.5, 0.5);
sellItemButton.addChild(sellItemText);
sellItemButton.x = 200;
sellItemContainer.addChild(sellItemButton);
// Create closure for button handler
(function (itemType) {
sellItemButton.down = function (x, y, obj) {
sellItemBg.tint = 0x666666;
// Sell this item type
if (inventory[itemType] > 0) {
var value = inventory[itemType] * getItemValue(itemType);
LK.setScore(LK.getScore() + value);
inventory[itemType] = 0;
// Flash effect
tween(sellItemButton, {
alpha: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(sellItemButton, {
alpha: 1
}, {
duration: 200
});
}
});
// Refresh the selling interface
LK.setTimeout(function () {
self.showSellingInterface();
}, 400);
}
};
sellItemButton.up = function (x, y, obj) {
sellItemBg.tint = 0xFFFFFF;
};
})(item);
sellItemContainer.y = yPos;
self.contentArea.addChild(sellItemContainer);
yPos += 60;
}
}
if (!hasItems) {
var noItemsText = new Text2('No items to sell!', {
size: 24,
fill: 0xFFFFFF
});
noItemsText.anchor.set(0.5, 0.5);
self.contentArea.addChild(noItemsText);
}
};
self.visible = false;
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
var playerVerticalGraphics = self.attachAsset('playerVertical', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
playerVerticalGraphics.visible = false; // Hide vertical sprite initially
var playerDownGraphics = self.attachAsset('playerDown', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
playerDownGraphics.visible = false; // Hide down sprite initially
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 3;
self.attackRange = 80;
self.attackDamage = 25;
self.lastAttackTime = 0;
self.attackCooldown = 500;
self.facingDirection = 'right'; // Track which direction player is facing
self.lastX = 0; // Track last position to determine movement direction
self.lastY = 0;
// Method to update sprite direction based on facing
self.updateSpriteDirection = function () {
if (self.facingDirection === 'left') {
playerGraphics.visible = true;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = false;
playerGraphics.scale.x = -1; // Flip sprite horizontally
playerGraphics.rotation = 0; // No rotation
} else if (self.facingDirection === 'right') {
playerGraphics.visible = true;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = false;
playerGraphics.scale.x = 1; // Normal sprite orientation
playerGraphics.rotation = 0; // No rotation
} else if (self.facingDirection === 'up') {
playerGraphics.visible = false;
playerVerticalGraphics.visible = true;
playerDownGraphics.visible = false;
playerVerticalGraphics.scale.x = 1; // Normal scale
playerVerticalGraphics.rotation = 0; // No rotation - use original up sprite
} else if (self.facingDirection === 'down') {
playerGraphics.visible = false;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = true;
playerDownGraphics.scale.x = 1; // Normal scale
playerDownGraphics.rotation = 0; // No rotation - use separate down sprite
}
};
// Method to set facing direction and update sprite
self.setFacingDirection = function (direction) {
if (direction === 'left' || direction === 'right' || direction === 'up' || direction === 'down') {
self.facingDirection = direction;
self.updateSpriteDirection();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('hurt').play();
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
self.canAttack = function () {
return LK.ticks - self.lastAttackTime > self.attackCooldown;
};
self.canMoveTo = function (x, y) {
return !checkCollisionWithSolids(x, y, 15);
};
self.update = function () {
// Detect movement direction and update facing
if (self.x !== self.lastX) {
if (self.x > self.lastX) {
self.setFacingDirection('right');
} else if (self.x < self.lastX) {
self.setFacingDirection('left');
}
} else if (self.y !== self.lastY) {
if (self.y > self.lastY) {
self.setFacingDirection('down');
} else if (self.y < self.lastY) {
self.setFacingDirection('up');
}
}
// Update last position for next frame
self.lastX = self.x;
self.lastY = self.y;
};
self.attack = function () {
if (!self.canAttack()) return;
self.lastAttackTime = LK.ticks;
LK.getSound('attack').play();
// Check for slimes in attack range with line of sight
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
var dx = slime.x - self.x;
var dy = slime.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange && hasLineOfSight(self.x, self.y, slime.x, slime.y)) {
slime.takeDamage(self.attackDamage);
}
}
// Check for rocks in attack range with line of sight
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
var dx = rock.x - self.x;
var dy = rock.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange && hasLineOfSight(self.x, self.y, rock.x, rock.y)) {
rock.hit();
}
}
};
return self;
});
var Resource = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.collected = false;
var resourceGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
width: 32,
height: 32
});
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
// Update inventory
if (!inventory[self.type]) {
inventory[self.type] = 0;
}
inventory[self.type]++;
// Remove from resources array
for (var i = resources.length - 1; i >= 0; i--) {
if (resources[i] === self) {
resources.splice(i, 1);
break;
}
}
game.removeChild(self);
};
return self;
});
var Rock = Container.expand(function (rockType) {
var self = Container.call(this);
self.rockType = rockType || 'rock';
var rockGraphics = self.attachAsset(self.rockType, {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.maxHealth = 3;
self.health = self.maxHealth;
self.destroyed = false;
self.mined = false;
self.hit = function () {
if (self.destroyed) return;
self.health--;
LK.getSound('mine_hit').play();
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
self.mine();
}
};
self.mine = function () {
if (self.mined || self.destroyed) return;
self.mined = true;
// Animate to half size and make collectable
tween(rockGraphics, {
width: 32,
height: 32
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Now it becomes a collectable resource
self.collectable = true;
// If this rock has the ladder, reveal it underneath
if (self.hasLadder && !ladder) {
ladder = new Ladder();
ladder.x = self.x;
ladder.y = self.y;
game.addChild(ladder);
}
}
});
};
self.destroy = function () {
if (self.destroyed) return;
self.destroyed = true;
// Drop resources based on rock type
var resource;
if (self.rockType === 'coal') {
resource = new Resource('coal');
} else if (self.rockType === 'copper_ore') {
resource = new Resource('copper_ore');
} else if (self.rockType === 'iron_ore') {
resource = new Resource('iron_ore');
} else if (self.rockType === 'gold_ore') {
resource = new Resource('gold_ore');
} else if (self.rockType === 'gem') {
resource = new Resource('gem');
} else {
// Regular rock always drops a rock resource
resource = new Resource('rock');
}
if (resource) {
resource.x = self.x;
resource.y = self.y;
resources.push(resource);
game.addChild(resource);
}
// Ladder is now spawned when mining the pre-determined ladder rock, not randomly on destroy
// Remove from rocks array
for (var i = rocks.length - 1; i >= 0; i--) {
if (rocks[i] === self) {
rocks.splice(i, 1);
break;
}
}
game.removeChild(self);
};
self.collect = function () {
if (!self.collectable || self.destroyed) return;
LK.getSound('collect').play();
// Update inventory
if (!inventory[self.rockType]) {
inventory[self.rockType] = 0;
}
inventory[self.rockType]++;
self.destroy();
};
return self;
});
var Slime = Container.expand(function () {
var self = Container.call(this);
var slimeGraphics = self.attachAsset('slime', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 1;
self.attackDamage = 15;
self.lastAttackTime = 0;
self.attackCooldown = 1000;
self.attackRange = 50;
self.lastDamageTime = 0;
self.damageInterval = 100; // 0.1 seconds between damage ticks
self.destroyed = false;
self.takeDamage = function (damage) {
if (self.destroyed) return;
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.destroy();
}
};
self.destroy = function () {
if (self.destroyed) return;
self.destroyed = true;
// Always drop slime item
var slimeResource = new Resource('slime');
slimeResource.x = self.x;
slimeResource.y = self.y;
resources.push(slimeResource);
game.addChild(slimeResource);
// Drop heart sometimes
if (Math.random() < 0.3) {
var heart = new HealthItem();
heart.x = self.x;
heart.y = self.y;
healthItems.push(heart);
game.addChild(heart);
}
// Remove from slimes array
for (var i = slimes.length - 1; i >= 0; i--) {
if (slimes[i] === self) {
slimes.splice(i, 1);
break;
}
}
// Increase score
LK.setScore(LK.getScore() + 10);
game.removeChild(self);
};
self.update = function () {
if (self.destroyed) return;
// Check if player is visible (line of sight)
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (hasLineOfSight(self.x, self.y, player.x, player.y) && distance < 300) {
// Move towards player if visible and within detection range in any direction (including diagonals)
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision before moving (only with solids and other slimes, not player)
if (!checkCollisionWithSolids(newX, newY, 15) && !checkCollisionWithSlimes(newX, newY, 15, self)) {
self.x = newX;
self.y = newY;
}
// Apply damage if touching player in any direction (including diagonals)
if (distance <= 50 && LK.ticks - self.lastDamageTime > self.damageInterval) {
self.lastDamageTime = LK.ticks;
player.takeDamage(Math.floor(player.maxHealth * 0.05)); // 5% damage
}
}
};
return self;
});
var ToolMerchant = Container.expand(function () {
var self = Container.call(this);
var npcGraphics = self.attachAsset('toolMerchantNPC', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
self.down = function (x, y, obj) {
// Open tool merchant interface
if (toolMerchantInterface) {
toolMerchantInterface.visible = true;
}
};
return self;
});
var ToolMerchantInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 600
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5,
width: 820,
height: 620
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Tool Shop', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -280;
self.addChild(titleText);
// Content area for tools
self.contentArea = new Container();
self.contentArea.y = -100;
self.addChild(self.contentArea);
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
var closeText = new Text2('X', {
size: 30,
fill: 0xFF0000
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 350;
closeButton.y = -250;
self.addChild(closeButton);
closeButton.down = function (x, y, obj) {
closeBg.tint = 0x666666;
self.visible = false;
};
closeButton.up = function (x, y, obj) {
closeBg.tint = 0xFFFFFF;
};
// Show available tools
self.showTools = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var tools = [{
item: 'pickaxe',
material: 'Stone',
price: 10
}, {
item: 'pickaxe',
material: 'Copper',
price: 25
}, {
item: 'pickaxe',
material: 'Iron',
price: 50
}, {
item: 'pickaxe',
material: 'Gold',
price: 100
}, {
item: 'sword',
material: 'Stone',
price: 15
}, {
item: 'sword',
material: 'Copper',
price: 30
}, {
item: 'sword',
material: 'Iron',
price: 60
}, {
item: 'sword',
material: 'Gold',
price: 120
}];
var yPos = 0;
for (var i = 0; i < tools.length; i++) {
var toolContainer = new Container();
// Tool sprite
var toolSprite = LK.getAsset(tools[i].item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
toolSprite.x = -150;
// Apply material tint
if (tools[i].material === 'Copper') {
toolSprite.tint = 0xB87333;
} else if (tools[i].material === 'Iron') {
toolSprite.tint = 0x778899;
} else if (tools[i].material === 'Gold') {
toolSprite.tint = 0xFFD700;
} else {
toolSprite.tint = 0x808080; // Stone
}
toolContainer.addChild(toolSprite);
// Tool info text
var infoText = new Text2(tools[i].material + ' ' + tools[i].item + ' - ' + tools[i].price + ' coins', {
size: 20,
fill: 0xFFFFFF
});
infoText.anchor.set(0, 0.5);
infoText.x = -100;
toolContainer.addChild(infoText);
// Buy button
var buyButton = new Container();
var buyBg = buyButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 40
});
var buyText = new Text2('Buy', {
size: 18,
fill: 0xFFFFFF
});
buyText.anchor.set(0.5, 0.5);
buyButton.addChild(buyText);
buyButton.x = 200;
toolContainer.addChild(buyButton);
// Create closure for button handler
(function (tool) {
buyButton.down = function (x, y, obj) {
buyBg.tint = 0x666666;
// Check if player has enough coins
var currentCoins = LK.getScore();
if (currentCoins >= tool.price) {
// Deduct coins
LK.setScore(currentCoins - tool.price);
// Add tool to inventory
if (!inventory[tool.material.toLowerCase() + '_' + tool.item]) {
inventory[tool.material.toLowerCase() + '_' + tool.item] = 0;
}
inventory[tool.material.toLowerCase() + '_' + tool.item]++;
// Flash effect
tween(buyButton, {
alpha: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(buyButton, {
alpha: 1
}, {
duration: 200
});
}
});
} else {
// Not enough coins - flash red
tween(buyButton, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(buyButton, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
};
buyButton.up = function (x, y, obj) {
buyBg.tint = 0xFFFFFF;
};
})(tools[i]);
toolContainer.y = yPos;
self.contentArea.addChild(toolContainer);
yPos += 60;
}
};
// Initialize with tools display
self.showTools();
self.visible = false;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F1B14
});
/****
* Game Code
****/
var player;
var rocks = [];
var slimes = [];
var resources = [];
var healthItems = [];
var ladder;
var inventory = {};
var currentFloor = 0; // Start at floor 0 (surface)
var walls = [];
var floorTiles = [];
var inventoryInterface;
var inventoryButton;
var merchant;
var forestObjects = [];
var merchantInterface;
var isInMine = false;
var locationStatus;
var coinDisplay;
var toolMerchant;
var toolMerchantInterface;
function checkCollisionWithWalls(x, y, radius) {
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = Math.abs(x - wall.x);
var dy = Math.abs(y - wall.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
}
return false;
}
function checkCollisionWithSolids(x, y, radius) {
// Check collision with walls
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = Math.abs(x - wall.x);
var dy = Math.abs(y - wall.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
}
// Check collision with rocks
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
if (!rock.destroyed && !rock.mined) {
var dx = Math.abs(x - rock.x);
var dy = Math.abs(y - rock.y);
if (dx < 16 + radius && dy < 16 + radius) {
return true;
}
}
}
// Check collision with forest objects (trees and bushes)
for (var i = 0; i < forestObjects.length; i++) {
var forestObj = forestObjects[i];
var dx = Math.abs(x - forestObj.x);
var dy = Math.abs(y - forestObj.y);
var objRadius = forestObj.width > 64 ? 48 : 24; // Trees are bigger
if (dx < objRadius + radius && dy < objRadius + radius) {
return true;
}
}
return false;
}
function checkCollisionWithSlimes(x, y, radius, excludeSlime) {
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
if (!slime.destroyed && slime !== excludeSlime) {
var dx = Math.abs(x - slime.x);
var dy = Math.abs(y - slime.y);
if (dx < 50 + radius && dy < 50 + radius) {
return true;
}
}
}
return false;
}
function checkCollisionWithPlayer(x, y, radius) {
if (!player) return false;
var dx = Math.abs(x - player.x);
var dy = Math.abs(y - player.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
return false;
}
function hasLineOfSight(x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
var distance = Math.sqrt(dx * dx + dy * dy);
var steps = Math.floor(distance / 16);
for (var i = 0; i <= steps; i++) {
var checkX = x1 + dx * i / steps;
var checkY = y1 + dy * i / steps;
if (checkCollisionWithSolids(checkX, checkY, 8)) {
return false;
}
}
return true;
}
// UI Elements
var healthBar;
var floorText;
var inventoryText;
function initializeGame() {
player = game.addChild(new Player());
if (currentFloor === 0) {
player.x = 1024;
player.y = 400; // Start on surface near cave entrance
} else {
player.x = 1024;
player.y = 300; // Start in mine near entrance
}
// Initialize position tracking
player.lastX = player.x;
player.lastY = player.y;
// Create UI - Health bar as horizontal bar (larger)
healthBar = new Container();
var healthBarBg = healthBar.attachAsset('wall', {
anchorX: 0,
anchorY: 0,
width: 400,
height: 30
});
healthBarBg.tint = 0x333333; // Dark background
var healthBarFill = healthBar.attachAsset('wall', {
anchorX: 0,
anchorY: 0,
width: 396,
height: 26
});
healthBarFill.tint = 0xFF0000; // Red health fill
healthBarFill.x = 2;
healthBarFill.y = 2;
healthBar.healthBarFill = healthBarFill; // Store reference for updates
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 130;
healthBar.y = 20;
floorText = new Container();
var floorSprite = floorText.attachAsset('ladder', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.floorSprite = floorSprite; // Store reference for updates
var floorNumber = new Text2('1', {
size: 40,
fill: 0xFFFFFF
});
floorNumber.anchor.set(0, 0);
floorNumber.x = 50;
floorNumber.y = 0;
floorText.addChild(floorNumber);
floorText.floorNumber = floorNumber; // Store reference for updates
var locationText = new Text2('', {
size: 30,
fill: 0xFFFFFF
});
locationText.anchor.set(0, 0);
locationText.x = 100;
locationText.y = 5;
floorText.addChild(locationText);
floorText.locationText = locationText; // Store reference for updates
LK.gui.top.addChild(floorText);
floorText.y = 20;
// Create location status indicator next to floor indicator
locationStatus = new Container();
var locationSprite = locationStatus.attachAsset('locationTown', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.locationSprite = locationSprite; // Store reference for updates
var locationStatusText = new Text2('pueblo', {
size: 30,
fill: 0xFFFFFF
});
locationStatusText.anchor.set(0, 0);
locationStatusText.x = 50;
locationStatusText.y = 5;
locationStatus.addChild(locationStatusText);
locationStatus.locationStatusText = locationStatusText; // Store reference for updates
LK.gui.top.addChild(locationStatus);
locationStatus.x = 200; // Position to the right of floor indicator
locationStatus.y = 20;
// Create coin display next to location status
coinDisplay = new Container();
var coinSprite = coinDisplay.attachAsset('coin', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
var coinText = new Text2('0', {
size: 30,
fill: 0xFFD700
});
coinText.anchor.set(0, 0);
coinText.x = 50;
coinText.y = 5;
coinDisplay.addChild(coinText);
coinDisplay.coinText = coinText; // Store reference for updates
LK.gui.top.addChild(coinDisplay);
coinDisplay.x = 400; // Position to the right of location status
coinDisplay.y = 20;
// Create inventory button
inventoryButton = new InventoryButton();
inventoryButton.x = -80;
inventoryButton.y = 80;
LK.gui.topRight.addChild(inventoryButton);
// Create inventory interface (initially hidden)
inventoryInterface = new InventoryInterface();
inventoryInterface.x = 0;
inventoryInterface.y = 0;
LK.gui.center.addChild(inventoryInterface);
// Create merchant interface (initially hidden)
merchantInterface = new MerchantInterface();
merchantInterface.x = 0;
merchantInterface.y = 0;
LK.gui.center.addChild(merchantInterface);
// Create tool merchant interface (initially hidden)
toolMerchantInterface = new ToolMerchantInterface();
toolMerchantInterface.x = 0;
toolMerchantInterface.y = 0;
LK.gui.center.addChild(toolMerchantInterface);
generateFloor();
// Create directional control buttons in bottom left
var buttonSize = 80;
var buttonSpacing = 90;
var baseX = 120;
var baseY = -200;
// Create up arrow button
var upButton = new DirectionalButton('Up');
upButton.x = baseX;
upButton.y = baseY - buttonSpacing;
LK.gui.bottomLeft.addChild(upButton);
// Create down arrow button
var downButton = new DirectionalButton('Down');
downButton.x = baseX;
downButton.y = baseY + buttonSpacing;
LK.gui.bottomLeft.addChild(downButton);
// Create left arrow button
var leftButton = new DirectionalButton('Left');
leftButton.x = baseX - buttonSpacing;
leftButton.y = baseY;
LK.gui.bottomLeft.addChild(leftButton);
// Create right arrow button
var rightButton = new DirectionalButton('Right');
rightButton.x = baseX + buttonSpacing;
rightButton.y = baseY;
LK.gui.bottomLeft.addChild(rightButton);
// Create action buttons on the right side
var actionBaseX = -120;
var actionBaseY = -200;
// Create pickaxe button
var pickaxeButton = new ActionButton('pickaxe');
pickaxeButton.x = actionBaseX;
pickaxeButton.y = actionBaseY - buttonSpacing / 2;
LK.gui.bottomRight.addChild(pickaxeButton);
// Create sword button
var swordButton = new ActionButton('sword');
swordButton.x = actionBaseX;
swordButton.y = actionBaseY + buttonSpacing / 2;
LK.gui.bottomRight.addChild(swordButton);
}
function generateFloor() {
// Clear existing objects
clearFloor();
if (currentFloor === 0) {
// Generate surface floor (floor 0)
generateSurface();
return;
}
// Generate floor tiles for grid visualization
for (var x = 64; x < 2048; x += 64) {
for (var y = 196; y < 2700; y += 64) {
var floorTile = new FloorTile();
floorTile.x = x;
floorTile.y = y;
floorTiles.push(floorTile);
game.addChild(floorTile);
}
}
// Generate perimeter walls
for (var x = 0; x < 2048; x += 64) {
var topWall = new Wall();
topWall.x = x + 32;
topWall.y = 132;
walls.push(topWall);
game.addChild(topWall);
var bottomWall = new Wall();
bottomWall.x = x + 32;
bottomWall.y = 2700;
walls.push(bottomWall);
game.addChild(bottomWall);
}
for (var y = 132; y < 2700; y += 64) {
var leftWall = new Wall();
leftWall.x = 32;
leftWall.y = y + 32;
walls.push(leftWall);
game.addChild(leftWall);
var rightWall = new Wall();
rightWall.x = 2016;
rightWall.y = y + 32;
walls.push(rightWall);
game.addChild(rightWall);
}
// Generate structured maze with corridors and chambers
generateMazeStructure();
// Pre-determine which rock will contain the ladder
var ladderRockIndex = Math.floor(Math.random() * (15 + Math.floor(currentFloor * 2)));
var ladderRock = null;
// Generate rocks with different types based on floor, placed in accessible areas
var numRocks = 15 + Math.floor(currentFloor * 2);
for (var i = 0; i < numRocks; i++) {
var rockType = 'rock'; // Default rock type
var rand = Math.random();
// Higher floors have better chances for rare materials
var floorMultiplier = Math.min(currentFloor / 10, 1); // Cap at floor 10
if (rand < 0.3) {
rockType = 'coal'; // Common on all floors
} else if (rand < 0.3 + 0.2 * floorMultiplier) {
rockType = 'copper_ore'; // More common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier) {
rockType = 'iron_ore'; // Rare, more common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier + 0.1 * floorMultiplier) {
rockType = 'gold_ore'; // Very rare, much more common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier + 0.1 * floorMultiplier + 0.05 * floorMultiplier) {
rockType = 'gem'; // Extremely rare, only on higher floors
}
var rock = new Rock(rockType);
var attempts = 0;
do {
// Generate grid-aligned positions (64x64 grid cells)
var gridX = Math.floor(Math.random() * 26) + 3; // Grid positions 3-28
var gridY = Math.floor(Math.random() * 32) + 5; // Grid positions 5-36
rock.x = 64 + gridX * 64; // Center in grid cell
rock.y = 196 + gridY * 64; // Center in grid cell
attempts++;
} while (checkCollisionWithSolids(rock.x, rock.y, 24) && attempts < 100);
// If we couldn't find a good spot after 100 attempts, place it along corridors
if (attempts >= 100) {
var corridorGridX = Math.floor(Math.random() * 18) + 6; // Corridor area grid
var corridorGridY = Math.floor(Math.random() * 20) + 9; // Corridor area grid
rock.x = 64 + corridorGridX * 64;
rock.y = 196 + corridorGridY * 64;
// Try a few more times in corridor area
var corridorAttempts = 0;
while (checkCollisionWithSolids(rock.x, rock.y, 24) && corridorAttempts < 20) {
corridorGridX = Math.floor(Math.random() * 18) + 6;
corridorGridY = Math.floor(Math.random() * 20) + 9;
rock.x = 64 + corridorGridX * 64;
rock.y = 196 + corridorGridY * 64;
corridorAttempts++;
}
}
// Mark this rock as the ladder rock if it's the chosen one
if (i === ladderRockIndex) {
rock.hasLadder = true;
ladderRock = rock;
}
rocks.push(rock);
game.addChild(rock);
}
// Generate slimes in accessible areas
var numSlimes = 3 + Math.floor(currentFloor * 1.5);
for (var i = 0; i < numSlimes; i++) {
var slime = new Slime();
var attempts = 0;
do {
slime.x = 200 + Math.random() * 1648;
slime.y = 300 + Math.random() * 2132;
attempts++;
} while (checkCollisionWithSolids(slime.x, slime.y, 20) && attempts < 100);
// If we couldn't find a good spot after 100 attempts, place it along corridors
if (attempts >= 100) {
slime.x = 400 + Math.random() * 1200;
slime.y = 600 + Math.random() * 1400;
// Try a few more times in corridor area
var corridorAttempts = 0;
while (checkCollisionWithSolids(slime.x, slime.y, 20) && corridorAttempts < 20) {
slime.x = 400 + Math.random() * 1200;
slime.y = 600 + Math.random() * 1400;
corridorAttempts++;
}
}
slime.maxHealth += Math.floor(currentFloor * 10);
slime.health = slime.maxHealth;
slimes.push(slime);
game.addChild(slime);
}
// Create portal at the top center (to return to surface)
var portal = LK.getAsset('portalEntrance', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
portal.x = 1024; // Center horizontally
portal.y = 300; // Consistent position with surface
portal.isPortal = true;
game.addChild(portal);
// Ladder will be generated when mining rocks
ladder = null;
// Update floor text and sprite based on location
if (currentFloor === 0) {
// On surface - use tree sprite and show "Pueblo Pochis"
floorText.removeChild(floorText.floorSprite);
floorText.floorSprite = floorText.attachAsset('tree', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.addChildAt(floorText.floorSprite, 0);
floorText.floorNumber.setText('0');
floorText.locationText.setText('Pueblo Pochis');
// Update location status indicator
locationStatus.removeChild(locationStatus.locationSprite);
locationStatus.locationSprite = locationStatus.attachAsset('locationTown', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.addChildAt(locationStatus.locationSprite, 0);
locationStatus.locationStatusText.setText('pueblo');
} else {
// In mine - use ladder sprite and show floor number
floorText.removeChild(floorText.floorSprite);
floorText.floorSprite = floorText.attachAsset('ladder', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.addChildAt(floorText.floorSprite, 0);
floorText.floorNumber.setText(currentFloor.toString());
floorText.locationText.setText('');
// Update location status indicator
locationStatus.removeChild(locationStatus.locationSprite);
locationStatus.locationSprite = locationStatus.attachAsset('locationMine', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.addChildAt(locationStatus.locationSprite, 0);
locationStatus.locationStatusText.setText('minas');
}
}
function clearFloor() {
// Remove all floor tiles
for (var i = 0; i < floorTiles.length; i++) {
game.removeChild(floorTiles[i]);
}
floorTiles = [];
// Remove all walls
for (var i = 0; i < walls.length; i++) {
game.removeChild(walls[i]);
}
walls = [];
// Remove all rocks
for (var i = 0; i < rocks.length; i++) {
game.removeChild(rocks[i]);
}
rocks = [];
// Remove all slimes
for (var i = 0; i < slimes.length; i++) {
game.removeChild(slimes[i]);
}
slimes = [];
// Remove all resources
for (var i = 0; i < resources.length; i++) {
game.removeChild(resources[i]);
}
resources = [];
// Remove all health items
for (var i = 0; i < healthItems.length; i++) {
game.removeChild(healthItems[i]);
}
healthItems = [];
// Remove ladder
if (ladder) {
game.removeChild(ladder);
ladder = null;
}
// Remove forest objects
for (var i = 0; i < forestObjects.length; i++) {
game.removeChild(forestObjects[i]);
}
forestObjects = [];
// Remove merchant
if (merchant) {
game.removeChild(merchant);
merchant = null;
}
// Remove tool merchant
if (toolMerchant) {
game.removeChild(toolMerchant);
toolMerchant = null;
}
}
function updateInventoryInterface() {
// Clear existing items from slots
for (var i = 0; i < inventoryInterface.itemSlots.length; i++) {
var slot = inventoryInterface.itemSlots[i];
// Remove all children except the background (first child)
while (slot.children.length > 1) {
slot.removeChild(slot.children[slot.children.length - 1]);
}
}
// Add items to slots
var slotIndex = 0;
for (var item in inventory) {
if (inventory[item] > 0 && slotIndex < inventoryInterface.itemSlots.length) {
var slot = inventoryInterface.itemSlots[slotIndex];
// Add item sprite
var itemSprite = LK.getAsset(item, {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50
});
itemSprite.x = 0;
itemSprite.y = -10;
slot.addChild(itemSprite);
// Add quantity text
var quantityText = new Text2(inventory[item].toString(), {
size: 20,
fill: 0xFFFFFF
});
quantityText.anchor.set(0.5, 0);
quantityText.x = 0;
quantityText.y = 20;
slot.addChild(quantityText);
slotIndex++;
}
}
}
function updateUI() {
// Update health bar - scale the fill based on health percentage (horizontal)
var healthPercent = player.health / player.maxHealth;
healthBar.healthBarFill.width = 396 * healthPercent;
// Change health bar color based on percentage
if (healthPercent > 0.66) {
tween(healthBar.healthBarFill, {
tint: 0x00FF00
}, {
duration: 200
}); // Green for healthy
} else if (healthPercent > 0.33) {
tween(healthBar.healthBarFill, {
tint: 0xFFFF00
}, {
duration: 200
}); // Yellow for medium health
} else {
tween(healthBar.healthBarFill, {
tint: 0xFF0000
}, {
duration: 200
}); // Red for low health
}
// Update coin display
if (coinDisplay && coinDisplay.coinText) {
coinDisplay.coinText.setText(LK.getScore().toString());
}
// Inventory is now handled by the inventory interface when opened
}
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = y;
// Keep player within bounds
newX = Math.max(64, Math.min(1984, newX));
newY = Math.max(164, Math.min(2668, newY));
// Check collision with walls and rocks
if (!checkCollisionWithSolids(newX, newY, 15)) {
var oldX = dragNode.x;
dragNode.x = newX;
dragNode.y = newY;
// Update player facing direction based on drag movement
if (dragNode === player) {
var oldY = dragNode.y;
if (newX !== oldX) {
if (newX > oldX) {
player.setFacingDirection('right');
} else if (newX < oldX) {
player.setFacingDirection('left');
}
} else if (newY !== oldY) {
if (newY > oldY) {
player.setFacingDirection('down');
} else if (newY < oldY) {
player.setFacingDirection('up');
}
}
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.update = function () {
if (!player) return;
// Update player
player.update();
// Update slimes
for (var i = 0; i < slimes.length; i++) {
slimes[i].update();
}
// Slimes now handle their own damage timing individually
// Automatic mining removed - now controlled by left click
// Check resource collection
for (var i = resources.length - 1; i >= 0; i--) {
var resource = resources[i];
var dx = resource.x - player.x;
var dy = resource.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
resource.collect();
}
}
// Check mined rock collection
for (var i = rocks.length - 1; i >= 0; i--) {
var rock = rocks[i];
if (rock.collectable && !rock.destroyed) {
var dx = rock.x - player.x;
var dy = rock.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
rock.collect();
}
}
}
// Check health item collection
for (var i = healthItems.length - 1; i >= 0; i--) {
var healthItem = healthItems[i];
var dx = healthItem.x - player.x;
var dy = healthItem.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
healthItem.collect();
}
}
// Check ladder interaction (only in mine floors)
if (ladder && !ladder.used && currentFloor > 0) {
var dx = ladder.x - player.x;
var dy = ladder.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
ladder.use();
}
}
// Check portal interactions
var allChildren = game.children;
for (var i = 0; i < allChildren.length; i++) {
var child = allChildren[i];
if (child.isPortal) {
var dx = child.x - player.x;
var dy = child.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
if (currentFloor === 0) {
// Enter mine (go to floor 1)
currentFloor = 1;
isInMine = true;
generateFloor();
// Spawn player 4 blocks (256 pixels) below the portal
player.x = 1024;
player.y = 300 + 256;
} else {
// Exit mine (go to pueblo pochis - floor 0)
currentFloor = 0;
isInMine = false;
generateFloor();
// Spawn player 4 blocks (256 pixels) below the portal
player.x = 1024;
player.y = 300 + 256;
}
break;
}
}
}
// Update UI
updateUI();
};
// Initialize the game
initializeGame();
function generateMazeStructure() {
// Create grid for maze generation (30x38 cells, each cell is 64x64 pixels)
var gridWidth = 30;
var gridHeight = 38;
var cellSize = 64;
var startX = 96;
var startY = 196;
// Create a grid to track where corridors are
var corridorGrid = [];
for (var x = 0; x < gridWidth; x++) {
corridorGrid[x] = [];
for (var y = 0; y < gridHeight; y++) {
corridorGrid[x][y] = false;
}
}
// Create main entrance tunnel from top-center
var entranceX = Math.floor(gridWidth / 2);
for (var y = 2; y < 8; y++) {
corridorGrid[entranceX][y] = true;
// Make entrance wider (3 tiles wide)
if (entranceX - 1 >= 0) corridorGrid[entranceX - 1][y] = true;
if (entranceX + 1 < gridWidth) corridorGrid[entranceX + 1][y] = true;
}
// Create main horizontal distribution corridor
var mainCorridorY = 8;
for (var x = 4; x < gridWidth - 4; x++) {
corridorGrid[x][mainCorridorY] = true;
// Make main corridor wider
corridorGrid[x][mainCorridorY + 1] = true;
}
// Create realistic mining tunnels branching from main corridor
var numMainTunnels = 4 + Math.floor(currentFloor / 3);
var tunnelSpacing = Math.floor((gridWidth - 8) / numMainTunnels);
for (var i = 0; i < numMainTunnels; i++) {
var tunnelX = 4 + i * tunnelSpacing + Math.floor(Math.random() * (tunnelSpacing / 2));
var tunnelLength = 8 + Math.floor(Math.random() * 12);
var tunnelStartY = mainCorridorY + 2;
// Create main mining tunnel going down
for (var y = tunnelStartY; y < Math.min(tunnelStartY + tunnelLength, gridHeight - 2); y++) {
corridorGrid[tunnelX][y] = true;
// Add slight width variation to make it more natural
if (Math.random() > 0.6) {
if (tunnelX - 1 >= 2) corridorGrid[tunnelX - 1][y] = true;
} else if (Math.random() > 0.6) {
if (tunnelX + 1 < gridWidth - 2) corridorGrid[tunnelX + 1][y] = true;
}
}
// Create mining chambers at the end of tunnels
if (tunnelStartY + tunnelLength < gridHeight - 4) {
var chamberY = tunnelStartY + tunnelLength - 2;
var chamberSize = 2 + Math.floor(Math.random() * 2); // 2x2 or 3x3 chamber
for (var cx = -chamberSize; cx <= chamberSize; cx++) {
for (var cy = -1; cy <= chamberSize; cy++) {
var newX = tunnelX + cx;
var newY = chamberY + cy;
if (newX >= 2 && newX < gridWidth - 2 && newY >= 2 && newY < gridHeight - 2) {
corridorGrid[newX][newY] = true;
}
}
}
}
// Create side branches from main tunnels (like real mine shafts)
var numSideBranches = 1 + Math.floor(Math.random() * 2);
for (var j = 0; j < numSideBranches; j++) {
var branchY = tunnelStartY + 3 + Math.floor(Math.random() * (tunnelLength - 6));
var branchDirection = Math.random() > 0.5 ? 1 : -1;
var branchLength = 3 + Math.floor(Math.random() * 5);
for (var k = 1; k <= branchLength; k++) {
var branchX = tunnelX + k * branchDirection;
if (branchX >= 2 && branchX < gridWidth - 2) {
corridorGrid[branchX][branchY] = true;
// Small chamber at end of side branch
if (k === branchLength) {
if (branchY - 1 >= 2) corridorGrid[branchX][branchY - 1] = true;
if (branchY + 1 < gridHeight - 2) corridorGrid[branchX][branchY + 1] = true;
}
}
}
}
}
// Create connecting tunnels between some main tunnels (crosscuts)
var numConnections = Math.floor(numMainTunnels / 2);
for (var i = 0; i < numConnections; i++) {
var connectionY = mainCorridorY + 5 + Math.floor(Math.random() * 10);
var startX = 4 + Math.floor(Math.random() * (gridWidth - 12));
var endX = startX + 4 + Math.floor(Math.random() * 6);
for (var x = startX; x <= Math.min(endX, gridWidth - 3); x++) {
if (connectionY >= 2 && connectionY < gridHeight - 2) {
corridorGrid[x][connectionY] = true;
}
}
}
// Convert corridor grid to walls (fill in non-corridor spaces)
for (var x = 2; x < gridWidth - 2; x++) {
for (var y = 2; y < gridHeight - 2; y++) {
if (!corridorGrid[x][y]) {
var wall = new Wall();
// Align wall to grid cell center (64x64 grid cells)
wall.x = 64 + x * cellSize; // Center in grid cell
wall.y = 196 + y * cellSize; // Center in grid cell
walls.push(wall);
game.addChild(wall);
}
}
}
// Add structural support pillars in wider areas
var numPillars = Math.floor(currentFloor / 3);
for (var i = 0; i < numPillars; i++) {
var pillarX = Math.floor(6 + Math.random() * (gridWidth - 12));
var pillarY = Math.floor(mainCorridorY + 3 + Math.random() * (gridHeight - mainCorridorY - 6));
// Only place pillar if it's in a corridor and won't block paths
if (corridorGrid[pillarX][pillarY]) {
var canPlace = true;
// Check if placing pillar would block critical paths
for (var dx = -1; dx <= 1 && canPlace; dx++) {
for (var dy = -1; dy <= 1 && canPlace; dy++) {
var checkX = pillarX + dx;
var checkY = pillarY + dy;
if (checkX >= 0 && checkX < gridWidth && checkY >= 0 && checkY < gridHeight) {
// Don't place if it would create a dead end
if (!corridorGrid[checkX][checkY] && (dx === 0 || dy === 0)) {
var adjacentCorridors = 0;
if (checkX > 0 && corridorGrid[checkX - 1][checkY]) adjacentCorridors++;
if (checkX < gridWidth - 1 && corridorGrid[checkX + 1][checkY]) adjacentCorridors++;
if (checkY > 0 && corridorGrid[checkX][checkY - 1]) adjacentCorridors++;
if (checkY < gridHeight - 1 && corridorGrid[checkX][checkY + 1]) adjacentCorridors++;
if (adjacentCorridors < 2) canPlace = false;
}
}
}
}
if (canPlace && Math.random() > 0.3) {
var wall = new Wall();
// Align pillar wall to grid cell center (64x64 grid cells)
wall.x = 64 + pillarX * cellSize; // Center in grid cell
wall.y = 196 + pillarY * cellSize; // Center in grid cell
walls.push(wall);
game.addChild(wall);
}
}
}
}
function generateSurface() {
// Generate grass floor tiles
for (var x = 64; x < 2048; x += 64) {
for (var y = 196; y < 2700; y += 64) {
var grassTile = LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
grassTile.x = x;
grassTile.y = y;
grassTile.alpha = 0.7;
floorTiles.push(grassTile);
game.addChild(grassTile);
}
}
// Ensure player renders above grass tiles by moving to front
if (player) {
game.removeChild(player);
game.addChild(player);
}
// Create consistent town layout
// Mine entrance (portal) at the north
var portal = LK.getAsset('portalEntrance', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
portal.x = 1024; // Center horizontally
portal.y = 300; // North position
portal.isPortal = true;
game.addChild(portal);
// Create main path from mine to town center (vertical stone path)
var pathTilePositions = [{
x: 1024,
y: 500
}, {
x: 1024,
y: 564
}, {
x: 1024,
y: 628
}, {
x: 1024,
y: 692
}, {
x: 1024,
y: 756
}, {
x: 1024,
y: 820
}, {
x: 1024,
y: 884
}, {
x: 1024,
y: 948
}, {
x: 1024,
y: 1012
}, {
x: 1024,
y: 1076
}, {
x: 1024,
y: 1140
}, {
x: 1024,
y: 1204
}, {
x: 1024,
y: 1268
}, {
x: 1024,
y: 1332
}, {
x: 1024,
y: 1396
}, {
x: 1024,
y: 1460
}];
// Add path tiles
for (var i = 0; i < pathTilePositions.length; i++) {
var pathTile = LK.getAsset('floorTile', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
pathTile.x = pathTilePositions[i].x;
pathTile.y = pathTilePositions[i].y;
pathTile.alpha = 0.8;
floorTiles.push(pathTile);
game.addChild(pathTile);
}
// Create houses along the path (consistent positions)
var housePositions = [{
x: 768,
y: 700
},
// Left side house 1
{
x: 1280,
y: 700
},
// Right side house 1
{
x: 768,
y: 900
},
// Left side house 2
{
x: 1280,
y: 900
},
// Right side house 2
{
x: 768,
y: 1100
},
// Left side house 3
{
x: 1280,
y: 1100
},
// Right side house 3
{
x: 768,
y: 1300
},
// Left side house 4
{
x: 1280,
y: 1300
} // Right side house 4
];
// Create houses using wall assets as house blocks
for (var i = 0; i < housePositions.length; i++) {
// Main house structure (3x3 blocks)
for (var hx = -1; hx <= 1; hx++) {
for (var hy = -1; hy <= 1; hy++) {
var houseBlock = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
houseBlock.x = housePositions[i].x + hx * 64;
houseBlock.y = housePositions[i].y + hy * 64;
houseBlock.tint = 0x8B4513; // Brown color for houses
forestObjects.push(houseBlock);
game.addChild(houseBlock);
}
}
}
// Position NPCs near houses consistently
merchant = new Merchant();
merchant.x = 700; // Near left side houses
merchant.y = 800;
game.addChild(merchant);
toolMerchant = new ToolMerchant();
toolMerchant.x = 1350; // Near right side houses
toolMerchant.y = 800;
game.addChild(toolMerchant);
// Place trees in organized clusters around the town perimeter
var treeClusterPositions = [
// Left forest cluster
{
x: 300,
y: 500
}, {
x: 364,
y: 500
}, {
x: 428,
y: 500
}, {
x: 300,
y: 600
}, {
x: 364,
y: 600
}, {
x: 428,
y: 600
}, {
x: 300,
y: 700
}, {
x: 364,
y: 700
}, {
x: 428,
y: 700
},
// Right forest cluster
{
x: 1620,
y: 500
}, {
x: 1684,
y: 500
}, {
x: 1748,
y: 500
}, {
x: 1620,
y: 600
}, {
x: 1684,
y: 600
}, {
x: 1748,
y: 600
}, {
x: 1620,
y: 700
}, {
x: 1684,
y: 700
}, {
x: 1748,
y: 700
},
// Bottom forest clusters
{
x: 500,
y: 1600
}, {
x: 564,
y: 1600
}, {
x: 628,
y: 1600
}, {
x: 1420,
y: 1600
}, {
x: 1484,
y: 1600
}, {
x: 1548,
y: 1600
},
// Corner clusters
{
x: 200,
y: 300
}, {
x: 264,
y: 300
}, {
x: 200,
y: 364
}, {
x: 1784,
y: 300
}, {
x: 1848,
y: 300
}, {
x: 1784,
y: 364
}];
for (var i = 0; i < treeClusterPositions.length; i++) {
var tree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
width: 128,
height: 128
});
tree.x = treeClusterPositions[i].x;
tree.y = treeClusterPositions[i].y;
forestObjects.push(tree);
game.addChild(tree);
}
// Place bushes in organized pattern between trees and houses
var bushPositions = [
// Around left houses
{
x: 600,
y: 650
}, {
x: 600,
y: 750
}, {
x: 600,
y: 850
}, {
x: 600,
y: 950
}, {
x: 500,
y: 800
}, {
x: 500,
y: 1000
}, {
x: 500,
y: 1200
},
// Around right houses
{
x: 1450,
y: 650
}, {
x: 1450,
y: 750
}, {
x: 1450,
y: 850
}, {
x: 1450,
y: 950
}, {
x: 1550,
y: 800
}, {
x: 1550,
y: 1000
}, {
x: 1550,
y: 1200
},
// Along path edges
{
x: 900,
y: 600
}, {
x: 1150,
y: 600
}, {
x: 900,
y: 1000
}, {
x: 1150,
y: 1000
}, {
x: 900,
y: 1400
}, {
x: 1150,
y: 1400
},
// Scattered decorative bushes
{
x: 400,
y: 900
}, {
x: 1650,
y: 900
}, {
x: 400,
y: 1300
}, {
x: 1650,
y: 1300
}];
for (var i = 0; i < bushPositions.length; i++) {
var bush = LK.getAsset('bush', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
bush.x = bushPositions[i].x;
bush.y = bushPositions[i].y;
forestObjects.push(bush);
game.addChild(bush);
}
isInMine = false;
}
function openMerchantInterface() {
// Simple merchant interaction - sell all items
var totalValue = 0;
var itemsSold = [];
for (var item in inventory) {
if (inventory[item] > 0) {
var itemValue = getItemValue(item);
var itemTotal = inventory[item] * itemValue;
totalValue += itemTotal;
itemsSold.push(item + ' x' + inventory[item] + ' = ' + itemTotal + ' coins');
inventory[item] = 0; // Clear inventory
}
}
if (totalValue > 0) {
LK.setScore(LK.getScore() + totalValue);
// Visual feedback
LK.effects.flashScreen(0x00FF00, 500);
}
}
function getItemValue(itemType) {
switch (itemType) {
case 'rock':
return 1;
case 'coal':
return 3;
case 'copper_ore':
return 5;
case 'iron_ore':
return 8;
case 'gold_ore':
return 15;
case 'gem':
return 25;
case 'slime':
return 2;
default:
return 1;
}
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ActionButton = Container.expand(function (actionType) {
var self = Container.call(this);
self.actionType = actionType;
var buttonGraphics = self.attachAsset(actionType, {
anchorX: 0.5,
anchorY: 0.5
});
// Action image is now visible without text overlay
self.down = function (x, y, obj) {
// Visual feedback - make button slightly darker when pressed
buttonGraphics.tint = 0x666666;
if (self.actionType === 'pickaxe') {
// Mining action - check for adjacent rocks
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
if (!rock.destroyed) {
var dx = rock.x - player.x;
var dy = rock.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 80) {
rock.hit();
break; // Mine only one rock per click
}
}
}
} else if (self.actionType === 'sword') {
// Attack action - check for adjacent slimes
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
if (!slime.destroyed) {
var dx = slime.x - player.x;
var dy = slime.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 80 && hasLineOfSight(player.x, player.y, slime.x, slime.y)) {
slime.takeDamage(34); // 3 hits to kill (100/3 = ~34 damage)
LK.getSound('attack').play();
break; // Attack only one slime per click
}
}
}
}
};
self.up = function (x, y, obj) {
// Reset button color when released
buttonGraphics.tint = 0xFFFFFF;
};
return self;
});
var DirectionalButton = Container.expand(function (direction) {
var self = Container.call(this);
self.direction = direction;
self.isPressed = false;
self.moveTimer = null;
var buttonGraphics = self.attachAsset('arrow' + direction.charAt(0).toUpperCase() + direction.slice(1), {
anchorX: 0.5,
anchorY: 0.5
});
// Method to move player in the button's direction
self.movePlayer = function () {
var moveDistance = 20; // Increased movement distance for faster speed
var newX = player.x;
var newY = player.y;
if (self.direction === 'Up') {
newY = player.y - moveDistance;
} else if (self.direction === 'Down') {
newY = player.y + moveDistance;
} else if (self.direction === 'Left') {
newX = player.x - moveDistance;
} else if (self.direction === 'Right') {
newX = player.x + moveDistance;
}
// Keep player within bounds
newX = Math.max(64, Math.min(1984, newX));
newY = Math.max(164, Math.min(2668, newY));
// Check collision before moving
if (!checkCollisionWithSolids(newX, newY, 15)) {
player.x = newX;
player.y = newY;
// Update player facing direction based on movement
if (self.direction === 'Left') {
player.setFacingDirection('left');
} else if (self.direction === 'Right') {
player.setFacingDirection('right');
} else if (self.direction === 'Up') {
player.setFacingDirection('up');
} else if (self.direction === 'Down') {
player.setFacingDirection('down');
}
}
};
// Arrow image is now visible without text overlay
self.down = function (x, y, obj) {
// Visual feedback - make button slightly darker when pressed
buttonGraphics.tint = 0x3A7BC8;
self.isPressed = true;
// Move immediately on press
self.movePlayer();
// Start continuous movement timer
self.moveTimer = LK.setInterval(function () {
if (self.isPressed) {
self.movePlayer();
}
}, 80); // Move every 80ms while pressed for faster speed
};
self.up = function (x, y, obj) {
// Reset button color when released
buttonGraphics.tint = 0xFFFFFF;
self.isPressed = false;
// Clear continuous movement timer
if (self.moveTimer) {
LK.clearInterval(self.moveTimer);
self.moveTimer = null;
}
};
return self;
});
var FloorTile = Container.expand(function () {
var self = Container.call(this);
var tileGraphics = self.attachAsset('floorTile', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
// Add subtle border to show grid lines
tileGraphics.alpha = 0.3;
return self;
});
var HealthItem = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
width: 32,
height: 32
});
self.collected = false;
self.healAmount = 25;
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
player.heal(self.healAmount);
// Remove from healthItems array
for (var i = healthItems.length - 1; i >= 0; i--) {
if (healthItems[i] === self) {
healthItems.splice(i, 1);
break;
}
}
game.removeChild(self);
};
return self;
});
var InventoryButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function (x, y, obj) {
buttonGraphics.tint = 0x666666;
// Toggle inventory interface
if (inventoryInterface.visible) {
inventoryInterface.visible = false;
} else {
inventoryInterface.visible = true;
updateInventoryInterface();
}
};
self.up = function (x, y, obj) {
buttonGraphics.tint = 0xFFFFFF;
};
return self;
});
var InventoryInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Inventory', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -180;
self.addChild(titleText);
self.itemSlots = [];
// Create grid of item slots (6x4 grid)
var slotSize = 80;
var slotSpacing = 90;
var startX = -225;
var startY = -120;
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 6; col++) {
var slotContainer = new Container();
// Slot background
var slotBg = LK.getAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5
});
slotContainer.addChild(slotBg);
slotContainer.x = startX + col * slotSpacing;
slotContainer.y = startY + row * slotSpacing;
self.addChild(slotContainer);
self.itemSlots.push(slotContainer);
}
}
self.visible = false;
return self;
});
var Ladder = Container.expand(function () {
var self = Container.call(this);
var ladderGraphics = self.attachAsset('ladder', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.used = false;
self.use = function () {
if (self.used) return;
self.used = true;
currentFloor++;
generateFloor();
};
return self;
});
var Merchant = Container.expand(function () {
var self = Container.call(this);
var npcGraphics = self.attachAsset('npc', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
self.down = function (x, y, obj) {
// Open merchant interface
if (merchantInterface) {
merchantInterface.visible = true;
merchantInterface.showPriceGuide(); // Show price guide by default
}
};
return self;
});
var MerchantInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 600
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5,
width: 820,
height: 620
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Merchant', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -280;
self.addChild(titleText);
// Price Guide Button
var priceGuideButton = new Container();
var priceGuideBg = priceGuideButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 60
});
var priceGuideText = new Text2('Price Guide', {
size: 24,
fill: 0xFFFFFF
});
priceGuideText.anchor.set(0.5, 0.5);
priceGuideButton.addChild(priceGuideText);
priceGuideButton.x = -150;
priceGuideButton.y = -200;
self.addChild(priceGuideButton);
// Sell Items Button
var sellButton = new Container();
var sellBg = sellButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 60
});
var sellText = new Text2('Sell Items', {
size: 24,
fill: 0xFFFFFF
});
sellText.anchor.set(0.5, 0.5);
sellButton.addChild(sellText);
sellButton.x = 150;
sellButton.y = -200;
self.addChild(sellButton);
// Content area for price guide or selling interface
self.contentArea = new Container();
self.contentArea.y = -100;
self.addChild(self.contentArea);
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
var closeText = new Text2('X', {
size: 30,
fill: 0xFF0000
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 350;
closeButton.y = -250;
self.addChild(closeButton);
// Button event handlers
priceGuideButton.down = function (x, y, obj) {
priceGuideBg.tint = 0x666666;
self.showPriceGuide();
};
priceGuideButton.up = function (x, y, obj) {
priceGuideBg.tint = 0xFFFFFF;
};
sellButton.down = function (x, y, obj) {
sellBg.tint = 0x666666;
self.showSellingInterface();
};
sellButton.up = function (x, y, obj) {
sellBg.tint = 0xFFFFFF;
};
closeButton.down = function (x, y, obj) {
closeBg.tint = 0x666666;
self.visible = false;
};
closeButton.up = function (x, y, obj) {
closeBg.tint = 0xFFFFFF;
};
// Show price guide
self.showPriceGuide = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var priceItems = [{
item: 'rock',
price: 1
}, {
item: 'coal',
price: 3
}, {
item: 'copper_ore',
price: 5
}, {
item: 'iron_ore',
price: 8
}, {
item: 'gold_ore',
price: 15
}, {
item: 'gem',
price: 25
}, {
item: 'slime',
price: 2
}];
var startY = 0;
for (var i = 0; i < priceItems.length; i++) {
var itemContainer = new Container();
// Item sprite
var itemSprite = LK.getAsset(priceItems[i].item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
itemSprite.x = -100;
itemContainer.addChild(itemSprite);
// Price text
var priceText = new Text2(priceItems[i].price + ' coins', {
size: 24,
fill: 0xFFFFFF
});
priceText.anchor.set(0, 0.5);
priceText.x = -50;
itemContainer.addChild(priceText);
itemContainer.y = startY + i * 50;
self.contentArea.addChild(itemContainer);
}
};
// Show selling interface
self.showSellingInterface = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var yPos = 0;
var hasItems = false;
for (var item in inventory) {
if (inventory[item] > 0) {
hasItems = true;
var sellItemContainer = new Container();
// Item sprite
var itemSprite = LK.getAsset(item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
itemSprite.x = -150;
sellItemContainer.addChild(itemSprite);
// Item info text
var itemValue = getItemValue(item);
var totalValue = inventory[item] * itemValue;
var infoText = new Text2(item + ' x' + inventory[item] + ' = ' + totalValue + ' coins', {
size: 20,
fill: 0xFFFFFF
});
infoText.anchor.set(0, 0.5);
infoText.x = -100;
sellItemContainer.addChild(infoText);
// Sell button for this item
var sellItemButton = new Container();
var sellItemBg = sellItemButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 40
});
var sellItemText = new Text2('Sell', {
size: 18,
fill: 0xFFFFFF
});
sellItemText.anchor.set(0.5, 0.5);
sellItemButton.addChild(sellItemText);
sellItemButton.x = 200;
sellItemContainer.addChild(sellItemButton);
// Create closure for button handler
(function (itemType) {
sellItemButton.down = function (x, y, obj) {
sellItemBg.tint = 0x666666;
// Sell this item type
if (inventory[itemType] > 0) {
var value = inventory[itemType] * getItemValue(itemType);
LK.setScore(LK.getScore() + value);
inventory[itemType] = 0;
// Flash effect
tween(sellItemButton, {
alpha: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(sellItemButton, {
alpha: 1
}, {
duration: 200
});
}
});
// Refresh the selling interface
LK.setTimeout(function () {
self.showSellingInterface();
}, 400);
}
};
sellItemButton.up = function (x, y, obj) {
sellItemBg.tint = 0xFFFFFF;
};
})(item);
sellItemContainer.y = yPos;
self.contentArea.addChild(sellItemContainer);
yPos += 60;
}
}
if (!hasItems) {
var noItemsText = new Text2('No items to sell!', {
size: 24,
fill: 0xFFFFFF
});
noItemsText.anchor.set(0.5, 0.5);
self.contentArea.addChild(noItemsText);
}
};
self.visible = false;
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
var playerVerticalGraphics = self.attachAsset('playerVertical', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
playerVerticalGraphics.visible = false; // Hide vertical sprite initially
var playerDownGraphics = self.attachAsset('playerDown', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
playerDownGraphics.visible = false; // Hide down sprite initially
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 3;
self.attackRange = 80;
self.attackDamage = 25;
self.lastAttackTime = 0;
self.attackCooldown = 500;
self.facingDirection = 'right'; // Track which direction player is facing
self.lastX = 0; // Track last position to determine movement direction
self.lastY = 0;
// Method to update sprite direction based on facing
self.updateSpriteDirection = function () {
if (self.facingDirection === 'left') {
playerGraphics.visible = true;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = false;
playerGraphics.scale.x = -1; // Flip sprite horizontally
playerGraphics.rotation = 0; // No rotation
} else if (self.facingDirection === 'right') {
playerGraphics.visible = true;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = false;
playerGraphics.scale.x = 1; // Normal sprite orientation
playerGraphics.rotation = 0; // No rotation
} else if (self.facingDirection === 'up') {
playerGraphics.visible = false;
playerVerticalGraphics.visible = true;
playerDownGraphics.visible = false;
playerVerticalGraphics.scale.x = 1; // Normal scale
playerVerticalGraphics.rotation = 0; // No rotation - use original up sprite
} else if (self.facingDirection === 'down') {
playerGraphics.visible = false;
playerVerticalGraphics.visible = false;
playerDownGraphics.visible = true;
playerDownGraphics.scale.x = 1; // Normal scale
playerDownGraphics.rotation = 0; // No rotation - use separate down sprite
}
};
// Method to set facing direction and update sprite
self.setFacingDirection = function (direction) {
if (direction === 'left' || direction === 'right' || direction === 'up' || direction === 'down') {
self.facingDirection = direction;
self.updateSpriteDirection();
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('hurt').play();
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
};
self.canAttack = function () {
return LK.ticks - self.lastAttackTime > self.attackCooldown;
};
self.canMoveTo = function (x, y) {
return !checkCollisionWithSolids(x, y, 15);
};
self.update = function () {
// Detect movement direction and update facing
if (self.x !== self.lastX) {
if (self.x > self.lastX) {
self.setFacingDirection('right');
} else if (self.x < self.lastX) {
self.setFacingDirection('left');
}
} else if (self.y !== self.lastY) {
if (self.y > self.lastY) {
self.setFacingDirection('down');
} else if (self.y < self.lastY) {
self.setFacingDirection('up');
}
}
// Update last position for next frame
self.lastX = self.x;
self.lastY = self.y;
};
self.attack = function () {
if (!self.canAttack()) return;
self.lastAttackTime = LK.ticks;
LK.getSound('attack').play();
// Check for slimes in attack range with line of sight
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
var dx = slime.x - self.x;
var dy = slime.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange && hasLineOfSight(self.x, self.y, slime.x, slime.y)) {
slime.takeDamage(self.attackDamage);
}
}
// Check for rocks in attack range with line of sight
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
var dx = rock.x - self.x;
var dy = rock.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange && hasLineOfSight(self.x, self.y, rock.x, rock.y)) {
rock.hit();
}
}
};
return self;
});
var Resource = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.collected = false;
var resourceGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
width: 32,
height: 32
});
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('collect').play();
// Update inventory
if (!inventory[self.type]) {
inventory[self.type] = 0;
}
inventory[self.type]++;
// Remove from resources array
for (var i = resources.length - 1; i >= 0; i--) {
if (resources[i] === self) {
resources.splice(i, 1);
break;
}
}
game.removeChild(self);
};
return self;
});
var Rock = Container.expand(function (rockType) {
var self = Container.call(this);
self.rockType = rockType || 'rock';
var rockGraphics = self.attachAsset(self.rockType, {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.maxHealth = 3;
self.health = self.maxHealth;
self.destroyed = false;
self.mined = false;
self.hit = function () {
if (self.destroyed) return;
self.health--;
LK.getSound('mine_hit').play();
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
self.mine();
}
};
self.mine = function () {
if (self.mined || self.destroyed) return;
self.mined = true;
// Animate to half size and make collectable
tween(rockGraphics, {
width: 32,
height: 32
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Now it becomes a collectable resource
self.collectable = true;
// If this rock has the ladder, reveal it underneath
if (self.hasLadder && !ladder) {
ladder = new Ladder();
ladder.x = self.x;
ladder.y = self.y;
game.addChild(ladder);
}
}
});
};
self.destroy = function () {
if (self.destroyed) return;
self.destroyed = true;
// Drop resources based on rock type
var resource;
if (self.rockType === 'coal') {
resource = new Resource('coal');
} else if (self.rockType === 'copper_ore') {
resource = new Resource('copper_ore');
} else if (self.rockType === 'iron_ore') {
resource = new Resource('iron_ore');
} else if (self.rockType === 'gold_ore') {
resource = new Resource('gold_ore');
} else if (self.rockType === 'gem') {
resource = new Resource('gem');
} else {
// Regular rock always drops a rock resource
resource = new Resource('rock');
}
if (resource) {
resource.x = self.x;
resource.y = self.y;
resources.push(resource);
game.addChild(resource);
}
// Ladder is now spawned when mining the pre-determined ladder rock, not randomly on destroy
// Remove from rocks array
for (var i = rocks.length - 1; i >= 0; i--) {
if (rocks[i] === self) {
rocks.splice(i, 1);
break;
}
}
game.removeChild(self);
};
self.collect = function () {
if (!self.collectable || self.destroyed) return;
LK.getSound('collect').play();
// Update inventory
if (!inventory[self.rockType]) {
inventory[self.rockType] = 0;
}
inventory[self.rockType]++;
self.destroy();
};
return self;
});
var Slime = Container.expand(function () {
var self = Container.call(this);
var slimeGraphics = self.attachAsset('slime', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 1;
self.attackDamage = 15;
self.lastAttackTime = 0;
self.attackCooldown = 1000;
self.attackRange = 50;
self.lastDamageTime = 0;
self.damageInterval = 100; // 0.1 seconds between damage ticks
self.destroyed = false;
self.takeDamage = function (damage) {
if (self.destroyed) return;
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
self.destroy();
}
};
self.destroy = function () {
if (self.destroyed) return;
self.destroyed = true;
// Always drop slime item
var slimeResource = new Resource('slime');
slimeResource.x = self.x;
slimeResource.y = self.y;
resources.push(slimeResource);
game.addChild(slimeResource);
// Drop heart sometimes
if (Math.random() < 0.3) {
var heart = new HealthItem();
heart.x = self.x;
heart.y = self.y;
healthItems.push(heart);
game.addChild(heart);
}
// Remove from slimes array
for (var i = slimes.length - 1; i >= 0; i--) {
if (slimes[i] === self) {
slimes.splice(i, 1);
break;
}
}
// Increase score
LK.setScore(LK.getScore() + 10);
game.removeChild(self);
};
self.update = function () {
if (self.destroyed) return;
// Check if player is visible (line of sight)
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (hasLineOfSight(self.x, self.y, player.x, player.y) && distance < 300) {
// Move towards player if visible and within detection range in any direction (including diagonals)
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision before moving (only with solids and other slimes, not player)
if (!checkCollisionWithSolids(newX, newY, 15) && !checkCollisionWithSlimes(newX, newY, 15, self)) {
self.x = newX;
self.y = newY;
}
// Apply damage if touching player in any direction (including diagonals)
if (distance <= 50 && LK.ticks - self.lastDamageTime > self.damageInterval) {
self.lastDamageTime = LK.ticks;
player.takeDamage(Math.floor(player.maxHealth * 0.05)); // 5% damage
}
}
};
return self;
});
var ToolMerchant = Container.expand(function () {
var self = Container.call(this);
var npcGraphics = self.attachAsset('toolMerchantNPC', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
self.down = function (x, y, obj) {
// Open tool merchant interface
if (toolMerchantInterface) {
toolMerchantInterface.visible = true;
}
};
return self;
});
var ToolMerchantInterface = Container.expand(function () {
var self = Container.call(this);
// Create background panel
var background = self.attachAsset('inventoryBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 600
});
// Create border
var border = self.attachAsset('inventoryBorder', {
anchorX: 0.5,
anchorY: 0.5,
width: 820,
height: 620
});
self.addChildAt(border, 0);
// Title text
var titleText = new Text2('Tool Shop', {
size: 40,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 0;
titleText.y = -280;
self.addChild(titleText);
// Content area for tools
self.contentArea = new Container();
self.contentArea.y = -100;
self.addChild(self.contentArea);
// Close button
var closeButton = new Container();
var closeBg = closeButton.attachAsset('inventoryButton', {
anchorX: 0.5,
anchorY: 0.5
});
var closeText = new Text2('X', {
size: 30,
fill: 0xFF0000
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 350;
closeButton.y = -250;
self.addChild(closeButton);
closeButton.down = function (x, y, obj) {
closeBg.tint = 0x666666;
self.visible = false;
};
closeButton.up = function (x, y, obj) {
closeBg.tint = 0xFFFFFF;
};
// Show available tools
self.showTools = function () {
// Clear content area
while (self.contentArea.children.length > 0) {
self.contentArea.removeChild(self.contentArea.children[0]);
}
var tools = [{
item: 'pickaxe',
material: 'Stone',
price: 10
}, {
item: 'pickaxe',
material: 'Copper',
price: 25
}, {
item: 'pickaxe',
material: 'Iron',
price: 50
}, {
item: 'pickaxe',
material: 'Gold',
price: 100
}, {
item: 'sword',
material: 'Stone',
price: 15
}, {
item: 'sword',
material: 'Copper',
price: 30
}, {
item: 'sword',
material: 'Iron',
price: 60
}, {
item: 'sword',
material: 'Gold',
price: 120
}];
var yPos = 0;
for (var i = 0; i < tools.length; i++) {
var toolContainer = new Container();
// Tool sprite
var toolSprite = LK.getAsset(tools[i].item, {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 40
});
toolSprite.x = -150;
// Apply material tint
if (tools[i].material === 'Copper') {
toolSprite.tint = 0xB87333;
} else if (tools[i].material === 'Iron') {
toolSprite.tint = 0x778899;
} else if (tools[i].material === 'Gold') {
toolSprite.tint = 0xFFD700;
} else {
toolSprite.tint = 0x808080; // Stone
}
toolContainer.addChild(toolSprite);
// Tool info text
var infoText = new Text2(tools[i].material + ' ' + tools[i].item + ' - ' + tools[i].price + ' coins', {
size: 20,
fill: 0xFFFFFF
});
infoText.anchor.set(0, 0.5);
infoText.x = -100;
toolContainer.addChild(infoText);
// Buy button
var buyButton = new Container();
var buyBg = buyButton.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 40
});
var buyText = new Text2('Buy', {
size: 18,
fill: 0xFFFFFF
});
buyText.anchor.set(0.5, 0.5);
buyButton.addChild(buyText);
buyButton.x = 200;
toolContainer.addChild(buyButton);
// Create closure for button handler
(function (tool) {
buyButton.down = function (x, y, obj) {
buyBg.tint = 0x666666;
// Check if player has enough coins
var currentCoins = LK.getScore();
if (currentCoins >= tool.price) {
// Deduct coins
LK.setScore(currentCoins - tool.price);
// Add tool to inventory
if (!inventory[tool.material.toLowerCase() + '_' + tool.item]) {
inventory[tool.material.toLowerCase() + '_' + tool.item] = 0;
}
inventory[tool.material.toLowerCase() + '_' + tool.item]++;
// Flash effect
tween(buyButton, {
alpha: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(buyButton, {
alpha: 1
}, {
duration: 200
});
}
});
} else {
// Not enough coins - flash red
tween(buyButton, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(buyButton, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
};
buyButton.up = function (x, y, obj) {
buyBg.tint = 0xFFFFFF;
};
})(tools[i]);
toolContainer.y = yPos;
self.contentArea.addChild(toolContainer);
yPos += 60;
}
};
// Initialize with tools display
self.showTools();
self.visible = false;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F1B14
});
/****
* Game Code
****/
var player;
var rocks = [];
var slimes = [];
var resources = [];
var healthItems = [];
var ladder;
var inventory = {};
var currentFloor = 0; // Start at floor 0 (surface)
var walls = [];
var floorTiles = [];
var inventoryInterface;
var inventoryButton;
var merchant;
var forestObjects = [];
var merchantInterface;
var isInMine = false;
var locationStatus;
var coinDisplay;
var toolMerchant;
var toolMerchantInterface;
function checkCollisionWithWalls(x, y, radius) {
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = Math.abs(x - wall.x);
var dy = Math.abs(y - wall.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
}
return false;
}
function checkCollisionWithSolids(x, y, radius) {
// Check collision with walls
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var dx = Math.abs(x - wall.x);
var dy = Math.abs(y - wall.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
}
// Check collision with rocks
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
if (!rock.destroyed && !rock.mined) {
var dx = Math.abs(x - rock.x);
var dy = Math.abs(y - rock.y);
if (dx < 16 + radius && dy < 16 + radius) {
return true;
}
}
}
// Check collision with forest objects (trees and bushes)
for (var i = 0; i < forestObjects.length; i++) {
var forestObj = forestObjects[i];
var dx = Math.abs(x - forestObj.x);
var dy = Math.abs(y - forestObj.y);
var objRadius = forestObj.width > 64 ? 48 : 24; // Trees are bigger
if (dx < objRadius + radius && dy < objRadius + radius) {
return true;
}
}
return false;
}
function checkCollisionWithSlimes(x, y, radius, excludeSlime) {
for (var i = 0; i < slimes.length; i++) {
var slime = slimes[i];
if (!slime.destroyed && slime !== excludeSlime) {
var dx = Math.abs(x - slime.x);
var dy = Math.abs(y - slime.y);
if (dx < 50 + radius && dy < 50 + radius) {
return true;
}
}
}
return false;
}
function checkCollisionWithPlayer(x, y, radius) {
if (!player) return false;
var dx = Math.abs(x - player.x);
var dy = Math.abs(y - player.y);
if (dx < 32 + radius && dy < 32 + radius) {
return true;
}
return false;
}
function hasLineOfSight(x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
var distance = Math.sqrt(dx * dx + dy * dy);
var steps = Math.floor(distance / 16);
for (var i = 0; i <= steps; i++) {
var checkX = x1 + dx * i / steps;
var checkY = y1 + dy * i / steps;
if (checkCollisionWithSolids(checkX, checkY, 8)) {
return false;
}
}
return true;
}
// UI Elements
var healthBar;
var floorText;
var inventoryText;
function initializeGame() {
player = game.addChild(new Player());
if (currentFloor === 0) {
player.x = 1024;
player.y = 400; // Start on surface near cave entrance
} else {
player.x = 1024;
player.y = 300; // Start in mine near entrance
}
// Initialize position tracking
player.lastX = player.x;
player.lastY = player.y;
// Create UI - Health bar as horizontal bar (larger)
healthBar = new Container();
var healthBarBg = healthBar.attachAsset('wall', {
anchorX: 0,
anchorY: 0,
width: 400,
height: 30
});
healthBarBg.tint = 0x333333; // Dark background
var healthBarFill = healthBar.attachAsset('wall', {
anchorX: 0,
anchorY: 0,
width: 396,
height: 26
});
healthBarFill.tint = 0xFF0000; // Red health fill
healthBarFill.x = 2;
healthBarFill.y = 2;
healthBar.healthBarFill = healthBarFill; // Store reference for updates
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 130;
healthBar.y = 20;
floorText = new Container();
var floorSprite = floorText.attachAsset('ladder', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.floorSprite = floorSprite; // Store reference for updates
var floorNumber = new Text2('1', {
size: 40,
fill: 0xFFFFFF
});
floorNumber.anchor.set(0, 0);
floorNumber.x = 50;
floorNumber.y = 0;
floorText.addChild(floorNumber);
floorText.floorNumber = floorNumber; // Store reference for updates
var locationText = new Text2('', {
size: 30,
fill: 0xFFFFFF
});
locationText.anchor.set(0, 0);
locationText.x = 100;
locationText.y = 5;
floorText.addChild(locationText);
floorText.locationText = locationText; // Store reference for updates
LK.gui.top.addChild(floorText);
floorText.y = 20;
// Create location status indicator next to floor indicator
locationStatus = new Container();
var locationSprite = locationStatus.attachAsset('locationTown', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.locationSprite = locationSprite; // Store reference for updates
var locationStatusText = new Text2('pueblo', {
size: 30,
fill: 0xFFFFFF
});
locationStatusText.anchor.set(0, 0);
locationStatusText.x = 50;
locationStatusText.y = 5;
locationStatus.addChild(locationStatusText);
locationStatus.locationStatusText = locationStatusText; // Store reference for updates
LK.gui.top.addChild(locationStatus);
locationStatus.x = 200; // Position to the right of floor indicator
locationStatus.y = 20;
// Create coin display next to location status
coinDisplay = new Container();
var coinSprite = coinDisplay.attachAsset('coin', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
var coinText = new Text2('0', {
size: 30,
fill: 0xFFD700
});
coinText.anchor.set(0, 0);
coinText.x = 50;
coinText.y = 5;
coinDisplay.addChild(coinText);
coinDisplay.coinText = coinText; // Store reference for updates
LK.gui.top.addChild(coinDisplay);
coinDisplay.x = 400; // Position to the right of location status
coinDisplay.y = 20;
// Create inventory button
inventoryButton = new InventoryButton();
inventoryButton.x = -80;
inventoryButton.y = 80;
LK.gui.topRight.addChild(inventoryButton);
// Create inventory interface (initially hidden)
inventoryInterface = new InventoryInterface();
inventoryInterface.x = 0;
inventoryInterface.y = 0;
LK.gui.center.addChild(inventoryInterface);
// Create merchant interface (initially hidden)
merchantInterface = new MerchantInterface();
merchantInterface.x = 0;
merchantInterface.y = 0;
LK.gui.center.addChild(merchantInterface);
// Create tool merchant interface (initially hidden)
toolMerchantInterface = new ToolMerchantInterface();
toolMerchantInterface.x = 0;
toolMerchantInterface.y = 0;
LK.gui.center.addChild(toolMerchantInterface);
generateFloor();
// Create directional control buttons in bottom left
var buttonSize = 80;
var buttonSpacing = 90;
var baseX = 120;
var baseY = -200;
// Create up arrow button
var upButton = new DirectionalButton('Up');
upButton.x = baseX;
upButton.y = baseY - buttonSpacing;
LK.gui.bottomLeft.addChild(upButton);
// Create down arrow button
var downButton = new DirectionalButton('Down');
downButton.x = baseX;
downButton.y = baseY + buttonSpacing;
LK.gui.bottomLeft.addChild(downButton);
// Create left arrow button
var leftButton = new DirectionalButton('Left');
leftButton.x = baseX - buttonSpacing;
leftButton.y = baseY;
LK.gui.bottomLeft.addChild(leftButton);
// Create right arrow button
var rightButton = new DirectionalButton('Right');
rightButton.x = baseX + buttonSpacing;
rightButton.y = baseY;
LK.gui.bottomLeft.addChild(rightButton);
// Create action buttons on the right side
var actionBaseX = -120;
var actionBaseY = -200;
// Create pickaxe button
var pickaxeButton = new ActionButton('pickaxe');
pickaxeButton.x = actionBaseX;
pickaxeButton.y = actionBaseY - buttonSpacing / 2;
LK.gui.bottomRight.addChild(pickaxeButton);
// Create sword button
var swordButton = new ActionButton('sword');
swordButton.x = actionBaseX;
swordButton.y = actionBaseY + buttonSpacing / 2;
LK.gui.bottomRight.addChild(swordButton);
}
function generateFloor() {
// Clear existing objects
clearFloor();
if (currentFloor === 0) {
// Generate surface floor (floor 0)
generateSurface();
return;
}
// Generate floor tiles for grid visualization
for (var x = 64; x < 2048; x += 64) {
for (var y = 196; y < 2700; y += 64) {
var floorTile = new FloorTile();
floorTile.x = x;
floorTile.y = y;
floorTiles.push(floorTile);
game.addChild(floorTile);
}
}
// Generate perimeter walls
for (var x = 0; x < 2048; x += 64) {
var topWall = new Wall();
topWall.x = x + 32;
topWall.y = 132;
walls.push(topWall);
game.addChild(topWall);
var bottomWall = new Wall();
bottomWall.x = x + 32;
bottomWall.y = 2700;
walls.push(bottomWall);
game.addChild(bottomWall);
}
for (var y = 132; y < 2700; y += 64) {
var leftWall = new Wall();
leftWall.x = 32;
leftWall.y = y + 32;
walls.push(leftWall);
game.addChild(leftWall);
var rightWall = new Wall();
rightWall.x = 2016;
rightWall.y = y + 32;
walls.push(rightWall);
game.addChild(rightWall);
}
// Generate structured maze with corridors and chambers
generateMazeStructure();
// Pre-determine which rock will contain the ladder
var ladderRockIndex = Math.floor(Math.random() * (15 + Math.floor(currentFloor * 2)));
var ladderRock = null;
// Generate rocks with different types based on floor, placed in accessible areas
var numRocks = 15 + Math.floor(currentFloor * 2);
for (var i = 0; i < numRocks; i++) {
var rockType = 'rock'; // Default rock type
var rand = Math.random();
// Higher floors have better chances for rare materials
var floorMultiplier = Math.min(currentFloor / 10, 1); // Cap at floor 10
if (rand < 0.3) {
rockType = 'coal'; // Common on all floors
} else if (rand < 0.3 + 0.2 * floorMultiplier) {
rockType = 'copper_ore'; // More common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier) {
rockType = 'iron_ore'; // Rare, more common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier + 0.1 * floorMultiplier) {
rockType = 'gold_ore'; // Very rare, much more common on higher floors
} else if (rand < 0.3 + 0.2 * floorMultiplier + 0.15 * floorMultiplier + 0.1 * floorMultiplier + 0.05 * floorMultiplier) {
rockType = 'gem'; // Extremely rare, only on higher floors
}
var rock = new Rock(rockType);
var attempts = 0;
do {
// Generate grid-aligned positions (64x64 grid cells)
var gridX = Math.floor(Math.random() * 26) + 3; // Grid positions 3-28
var gridY = Math.floor(Math.random() * 32) + 5; // Grid positions 5-36
rock.x = 64 + gridX * 64; // Center in grid cell
rock.y = 196 + gridY * 64; // Center in grid cell
attempts++;
} while (checkCollisionWithSolids(rock.x, rock.y, 24) && attempts < 100);
// If we couldn't find a good spot after 100 attempts, place it along corridors
if (attempts >= 100) {
var corridorGridX = Math.floor(Math.random() * 18) + 6; // Corridor area grid
var corridorGridY = Math.floor(Math.random() * 20) + 9; // Corridor area grid
rock.x = 64 + corridorGridX * 64;
rock.y = 196 + corridorGridY * 64;
// Try a few more times in corridor area
var corridorAttempts = 0;
while (checkCollisionWithSolids(rock.x, rock.y, 24) && corridorAttempts < 20) {
corridorGridX = Math.floor(Math.random() * 18) + 6;
corridorGridY = Math.floor(Math.random() * 20) + 9;
rock.x = 64 + corridorGridX * 64;
rock.y = 196 + corridorGridY * 64;
corridorAttempts++;
}
}
// Mark this rock as the ladder rock if it's the chosen one
if (i === ladderRockIndex) {
rock.hasLadder = true;
ladderRock = rock;
}
rocks.push(rock);
game.addChild(rock);
}
// Generate slimes in accessible areas
var numSlimes = 3 + Math.floor(currentFloor * 1.5);
for (var i = 0; i < numSlimes; i++) {
var slime = new Slime();
var attempts = 0;
do {
slime.x = 200 + Math.random() * 1648;
slime.y = 300 + Math.random() * 2132;
attempts++;
} while (checkCollisionWithSolids(slime.x, slime.y, 20) && attempts < 100);
// If we couldn't find a good spot after 100 attempts, place it along corridors
if (attempts >= 100) {
slime.x = 400 + Math.random() * 1200;
slime.y = 600 + Math.random() * 1400;
// Try a few more times in corridor area
var corridorAttempts = 0;
while (checkCollisionWithSolids(slime.x, slime.y, 20) && corridorAttempts < 20) {
slime.x = 400 + Math.random() * 1200;
slime.y = 600 + Math.random() * 1400;
corridorAttempts++;
}
}
slime.maxHealth += Math.floor(currentFloor * 10);
slime.health = slime.maxHealth;
slimes.push(slime);
game.addChild(slime);
}
// Create portal at the top center (to return to surface)
var portal = LK.getAsset('portalEntrance', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
portal.x = 1024; // Center horizontally
portal.y = 300; // Consistent position with surface
portal.isPortal = true;
game.addChild(portal);
// Ladder will be generated when mining rocks
ladder = null;
// Update floor text and sprite based on location
if (currentFloor === 0) {
// On surface - use tree sprite and show "Pueblo Pochis"
floorText.removeChild(floorText.floorSprite);
floorText.floorSprite = floorText.attachAsset('tree', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.addChildAt(floorText.floorSprite, 0);
floorText.floorNumber.setText('0');
floorText.locationText.setText('Pueblo Pochis');
// Update location status indicator
locationStatus.removeChild(locationStatus.locationSprite);
locationStatus.locationSprite = locationStatus.attachAsset('locationTown', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.addChildAt(locationStatus.locationSprite, 0);
locationStatus.locationStatusText.setText('pueblo');
} else {
// In mine - use ladder sprite and show floor number
floorText.removeChild(floorText.floorSprite);
floorText.floorSprite = floorText.attachAsset('ladder', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
floorText.addChildAt(floorText.floorSprite, 0);
floorText.floorNumber.setText(currentFloor.toString());
floorText.locationText.setText('');
// Update location status indicator
locationStatus.removeChild(locationStatus.locationSprite);
locationStatus.locationSprite = locationStatus.attachAsset('locationMine', {
anchorX: 0,
anchorY: 0,
width: 40,
height: 40
});
locationStatus.addChildAt(locationStatus.locationSprite, 0);
locationStatus.locationStatusText.setText('minas');
}
}
function clearFloor() {
// Remove all floor tiles
for (var i = 0; i < floorTiles.length; i++) {
game.removeChild(floorTiles[i]);
}
floorTiles = [];
// Remove all walls
for (var i = 0; i < walls.length; i++) {
game.removeChild(walls[i]);
}
walls = [];
// Remove all rocks
for (var i = 0; i < rocks.length; i++) {
game.removeChild(rocks[i]);
}
rocks = [];
// Remove all slimes
for (var i = 0; i < slimes.length; i++) {
game.removeChild(slimes[i]);
}
slimes = [];
// Remove all resources
for (var i = 0; i < resources.length; i++) {
game.removeChild(resources[i]);
}
resources = [];
// Remove all health items
for (var i = 0; i < healthItems.length; i++) {
game.removeChild(healthItems[i]);
}
healthItems = [];
// Remove ladder
if (ladder) {
game.removeChild(ladder);
ladder = null;
}
// Remove forest objects
for (var i = 0; i < forestObjects.length; i++) {
game.removeChild(forestObjects[i]);
}
forestObjects = [];
// Remove merchant
if (merchant) {
game.removeChild(merchant);
merchant = null;
}
// Remove tool merchant
if (toolMerchant) {
game.removeChild(toolMerchant);
toolMerchant = null;
}
}
function updateInventoryInterface() {
// Clear existing items from slots
for (var i = 0; i < inventoryInterface.itemSlots.length; i++) {
var slot = inventoryInterface.itemSlots[i];
// Remove all children except the background (first child)
while (slot.children.length > 1) {
slot.removeChild(slot.children[slot.children.length - 1]);
}
}
// Add items to slots
var slotIndex = 0;
for (var item in inventory) {
if (inventory[item] > 0 && slotIndex < inventoryInterface.itemSlots.length) {
var slot = inventoryInterface.itemSlots[slotIndex];
// Add item sprite
var itemSprite = LK.getAsset(item, {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50
});
itemSprite.x = 0;
itemSprite.y = -10;
slot.addChild(itemSprite);
// Add quantity text
var quantityText = new Text2(inventory[item].toString(), {
size: 20,
fill: 0xFFFFFF
});
quantityText.anchor.set(0.5, 0);
quantityText.x = 0;
quantityText.y = 20;
slot.addChild(quantityText);
slotIndex++;
}
}
}
function updateUI() {
// Update health bar - scale the fill based on health percentage (horizontal)
var healthPercent = player.health / player.maxHealth;
healthBar.healthBarFill.width = 396 * healthPercent;
// Change health bar color based on percentage
if (healthPercent > 0.66) {
tween(healthBar.healthBarFill, {
tint: 0x00FF00
}, {
duration: 200
}); // Green for healthy
} else if (healthPercent > 0.33) {
tween(healthBar.healthBarFill, {
tint: 0xFFFF00
}, {
duration: 200
}); // Yellow for medium health
} else {
tween(healthBar.healthBarFill, {
tint: 0xFF0000
}, {
duration: 200
}); // Red for low health
}
// Update coin display
if (coinDisplay && coinDisplay.coinText) {
coinDisplay.coinText.setText(LK.getScore().toString());
}
// Inventory is now handled by the inventory interface when opened
}
var dragNode = null;
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = y;
// Keep player within bounds
newX = Math.max(64, Math.min(1984, newX));
newY = Math.max(164, Math.min(2668, newY));
// Check collision with walls and rocks
if (!checkCollisionWithSolids(newX, newY, 15)) {
var oldX = dragNode.x;
dragNode.x = newX;
dragNode.y = newY;
// Update player facing direction based on drag movement
if (dragNode === player) {
var oldY = dragNode.y;
if (newX !== oldX) {
if (newX > oldX) {
player.setFacingDirection('right');
} else if (newX < oldX) {
player.setFacingDirection('left');
}
} else if (newY !== oldY) {
if (newY > oldY) {
player.setFacingDirection('down');
} else if (newY < oldY) {
player.setFacingDirection('up');
}
}
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.update = function () {
if (!player) return;
// Update player
player.update();
// Update slimes
for (var i = 0; i < slimes.length; i++) {
slimes[i].update();
}
// Slimes now handle their own damage timing individually
// Automatic mining removed - now controlled by left click
// Check resource collection
for (var i = resources.length - 1; i >= 0; i--) {
var resource = resources[i];
var dx = resource.x - player.x;
var dy = resource.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
resource.collect();
}
}
// Check mined rock collection
for (var i = rocks.length - 1; i >= 0; i--) {
var rock = rocks[i];
if (rock.collectable && !rock.destroyed) {
var dx = rock.x - player.x;
var dy = rock.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
rock.collect();
}
}
}
// Check health item collection
for (var i = healthItems.length - 1; i >= 0; i--) {
var healthItem = healthItems[i];
var dx = healthItem.x - player.x;
var dy = healthItem.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 40) {
healthItem.collect();
}
}
// Check ladder interaction (only in mine floors)
if (ladder && !ladder.used && currentFloor > 0) {
var dx = ladder.x - player.x;
var dy = ladder.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
ladder.use();
}
}
// Check portal interactions
var allChildren = game.children;
for (var i = 0; i < allChildren.length; i++) {
var child = allChildren[i];
if (child.isPortal) {
var dx = child.x - player.x;
var dy = child.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
if (currentFloor === 0) {
// Enter mine (go to floor 1)
currentFloor = 1;
isInMine = true;
generateFloor();
// Spawn player 4 blocks (256 pixels) below the portal
player.x = 1024;
player.y = 300 + 256;
} else {
// Exit mine (go to pueblo pochis - floor 0)
currentFloor = 0;
isInMine = false;
generateFloor();
// Spawn player 4 blocks (256 pixels) below the portal
player.x = 1024;
player.y = 300 + 256;
}
break;
}
}
}
// Update UI
updateUI();
};
// Initialize the game
initializeGame();
function generateMazeStructure() {
// Create grid for maze generation (30x38 cells, each cell is 64x64 pixels)
var gridWidth = 30;
var gridHeight = 38;
var cellSize = 64;
var startX = 96;
var startY = 196;
// Create a grid to track where corridors are
var corridorGrid = [];
for (var x = 0; x < gridWidth; x++) {
corridorGrid[x] = [];
for (var y = 0; y < gridHeight; y++) {
corridorGrid[x][y] = false;
}
}
// Create main entrance tunnel from top-center
var entranceX = Math.floor(gridWidth / 2);
for (var y = 2; y < 8; y++) {
corridorGrid[entranceX][y] = true;
// Make entrance wider (3 tiles wide)
if (entranceX - 1 >= 0) corridorGrid[entranceX - 1][y] = true;
if (entranceX + 1 < gridWidth) corridorGrid[entranceX + 1][y] = true;
}
// Create main horizontal distribution corridor
var mainCorridorY = 8;
for (var x = 4; x < gridWidth - 4; x++) {
corridorGrid[x][mainCorridorY] = true;
// Make main corridor wider
corridorGrid[x][mainCorridorY + 1] = true;
}
// Create realistic mining tunnels branching from main corridor
var numMainTunnels = 4 + Math.floor(currentFloor / 3);
var tunnelSpacing = Math.floor((gridWidth - 8) / numMainTunnels);
for (var i = 0; i < numMainTunnels; i++) {
var tunnelX = 4 + i * tunnelSpacing + Math.floor(Math.random() * (tunnelSpacing / 2));
var tunnelLength = 8 + Math.floor(Math.random() * 12);
var tunnelStartY = mainCorridorY + 2;
// Create main mining tunnel going down
for (var y = tunnelStartY; y < Math.min(tunnelStartY + tunnelLength, gridHeight - 2); y++) {
corridorGrid[tunnelX][y] = true;
// Add slight width variation to make it more natural
if (Math.random() > 0.6) {
if (tunnelX - 1 >= 2) corridorGrid[tunnelX - 1][y] = true;
} else if (Math.random() > 0.6) {
if (tunnelX + 1 < gridWidth - 2) corridorGrid[tunnelX + 1][y] = true;
}
}
// Create mining chambers at the end of tunnels
if (tunnelStartY + tunnelLength < gridHeight - 4) {
var chamberY = tunnelStartY + tunnelLength - 2;
var chamberSize = 2 + Math.floor(Math.random() * 2); // 2x2 or 3x3 chamber
for (var cx = -chamberSize; cx <= chamberSize; cx++) {
for (var cy = -1; cy <= chamberSize; cy++) {
var newX = tunnelX + cx;
var newY = chamberY + cy;
if (newX >= 2 && newX < gridWidth - 2 && newY >= 2 && newY < gridHeight - 2) {
corridorGrid[newX][newY] = true;
}
}
}
}
// Create side branches from main tunnels (like real mine shafts)
var numSideBranches = 1 + Math.floor(Math.random() * 2);
for (var j = 0; j < numSideBranches; j++) {
var branchY = tunnelStartY + 3 + Math.floor(Math.random() * (tunnelLength - 6));
var branchDirection = Math.random() > 0.5 ? 1 : -1;
var branchLength = 3 + Math.floor(Math.random() * 5);
for (var k = 1; k <= branchLength; k++) {
var branchX = tunnelX + k * branchDirection;
if (branchX >= 2 && branchX < gridWidth - 2) {
corridorGrid[branchX][branchY] = true;
// Small chamber at end of side branch
if (k === branchLength) {
if (branchY - 1 >= 2) corridorGrid[branchX][branchY - 1] = true;
if (branchY + 1 < gridHeight - 2) corridorGrid[branchX][branchY + 1] = true;
}
}
}
}
}
// Create connecting tunnels between some main tunnels (crosscuts)
var numConnections = Math.floor(numMainTunnels / 2);
for (var i = 0; i < numConnections; i++) {
var connectionY = mainCorridorY + 5 + Math.floor(Math.random() * 10);
var startX = 4 + Math.floor(Math.random() * (gridWidth - 12));
var endX = startX + 4 + Math.floor(Math.random() * 6);
for (var x = startX; x <= Math.min(endX, gridWidth - 3); x++) {
if (connectionY >= 2 && connectionY < gridHeight - 2) {
corridorGrid[x][connectionY] = true;
}
}
}
// Convert corridor grid to walls (fill in non-corridor spaces)
for (var x = 2; x < gridWidth - 2; x++) {
for (var y = 2; y < gridHeight - 2; y++) {
if (!corridorGrid[x][y]) {
var wall = new Wall();
// Align wall to grid cell center (64x64 grid cells)
wall.x = 64 + x * cellSize; // Center in grid cell
wall.y = 196 + y * cellSize; // Center in grid cell
walls.push(wall);
game.addChild(wall);
}
}
}
// Add structural support pillars in wider areas
var numPillars = Math.floor(currentFloor / 3);
for (var i = 0; i < numPillars; i++) {
var pillarX = Math.floor(6 + Math.random() * (gridWidth - 12));
var pillarY = Math.floor(mainCorridorY + 3 + Math.random() * (gridHeight - mainCorridorY - 6));
// Only place pillar if it's in a corridor and won't block paths
if (corridorGrid[pillarX][pillarY]) {
var canPlace = true;
// Check if placing pillar would block critical paths
for (var dx = -1; dx <= 1 && canPlace; dx++) {
for (var dy = -1; dy <= 1 && canPlace; dy++) {
var checkX = pillarX + dx;
var checkY = pillarY + dy;
if (checkX >= 0 && checkX < gridWidth && checkY >= 0 && checkY < gridHeight) {
// Don't place if it would create a dead end
if (!corridorGrid[checkX][checkY] && (dx === 0 || dy === 0)) {
var adjacentCorridors = 0;
if (checkX > 0 && corridorGrid[checkX - 1][checkY]) adjacentCorridors++;
if (checkX < gridWidth - 1 && corridorGrid[checkX + 1][checkY]) adjacentCorridors++;
if (checkY > 0 && corridorGrid[checkX][checkY - 1]) adjacentCorridors++;
if (checkY < gridHeight - 1 && corridorGrid[checkX][checkY + 1]) adjacentCorridors++;
if (adjacentCorridors < 2) canPlace = false;
}
}
}
}
if (canPlace && Math.random() > 0.3) {
var wall = new Wall();
// Align pillar wall to grid cell center (64x64 grid cells)
wall.x = 64 + pillarX * cellSize; // Center in grid cell
wall.y = 196 + pillarY * cellSize; // Center in grid cell
walls.push(wall);
game.addChild(wall);
}
}
}
}
function generateSurface() {
// Generate grass floor tiles
for (var x = 64; x < 2048; x += 64) {
for (var y = 196; y < 2700; y += 64) {
var grassTile = LK.getAsset('grass', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
grassTile.x = x;
grassTile.y = y;
grassTile.alpha = 0.7;
floorTiles.push(grassTile);
game.addChild(grassTile);
}
}
// Ensure player renders above grass tiles by moving to front
if (player) {
game.removeChild(player);
game.addChild(player);
}
// Create consistent town layout
// Mine entrance (portal) at the north
var portal = LK.getAsset('portalEntrance', {
anchorX: 0.5,
anchorY: 0.5,
width: 192,
height: 192
});
portal.x = 1024; // Center horizontally
portal.y = 300; // North position
portal.isPortal = true;
game.addChild(portal);
// Create main path from mine to town center (vertical stone path)
var pathTilePositions = [{
x: 1024,
y: 500
}, {
x: 1024,
y: 564
}, {
x: 1024,
y: 628
}, {
x: 1024,
y: 692
}, {
x: 1024,
y: 756
}, {
x: 1024,
y: 820
}, {
x: 1024,
y: 884
}, {
x: 1024,
y: 948
}, {
x: 1024,
y: 1012
}, {
x: 1024,
y: 1076
}, {
x: 1024,
y: 1140
}, {
x: 1024,
y: 1204
}, {
x: 1024,
y: 1268
}, {
x: 1024,
y: 1332
}, {
x: 1024,
y: 1396
}, {
x: 1024,
y: 1460
}];
// Add path tiles
for (var i = 0; i < pathTilePositions.length; i++) {
var pathTile = LK.getAsset('floorTile', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
pathTile.x = pathTilePositions[i].x;
pathTile.y = pathTilePositions[i].y;
pathTile.alpha = 0.8;
floorTiles.push(pathTile);
game.addChild(pathTile);
}
// Create houses along the path (consistent positions)
var housePositions = [{
x: 768,
y: 700
},
// Left side house 1
{
x: 1280,
y: 700
},
// Right side house 1
{
x: 768,
y: 900
},
// Left side house 2
{
x: 1280,
y: 900
},
// Right side house 2
{
x: 768,
y: 1100
},
// Left side house 3
{
x: 1280,
y: 1100
},
// Right side house 3
{
x: 768,
y: 1300
},
// Left side house 4
{
x: 1280,
y: 1300
} // Right side house 4
];
// Create houses using wall assets as house blocks
for (var i = 0; i < housePositions.length; i++) {
// Main house structure (3x3 blocks)
for (var hx = -1; hx <= 1; hx++) {
for (var hy = -1; hy <= 1; hy++) {
var houseBlock = LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
houseBlock.x = housePositions[i].x + hx * 64;
houseBlock.y = housePositions[i].y + hy * 64;
houseBlock.tint = 0x8B4513; // Brown color for houses
forestObjects.push(houseBlock);
game.addChild(houseBlock);
}
}
}
// Position NPCs near houses consistently
merchant = new Merchant();
merchant.x = 700; // Near left side houses
merchant.y = 800;
game.addChild(merchant);
toolMerchant = new ToolMerchant();
toolMerchant.x = 1350; // Near right side houses
toolMerchant.y = 800;
game.addChild(toolMerchant);
// Place trees in organized clusters around the town perimeter
var treeClusterPositions = [
// Left forest cluster
{
x: 300,
y: 500
}, {
x: 364,
y: 500
}, {
x: 428,
y: 500
}, {
x: 300,
y: 600
}, {
x: 364,
y: 600
}, {
x: 428,
y: 600
}, {
x: 300,
y: 700
}, {
x: 364,
y: 700
}, {
x: 428,
y: 700
},
// Right forest cluster
{
x: 1620,
y: 500
}, {
x: 1684,
y: 500
}, {
x: 1748,
y: 500
}, {
x: 1620,
y: 600
}, {
x: 1684,
y: 600
}, {
x: 1748,
y: 600
}, {
x: 1620,
y: 700
}, {
x: 1684,
y: 700
}, {
x: 1748,
y: 700
},
// Bottom forest clusters
{
x: 500,
y: 1600
}, {
x: 564,
y: 1600
}, {
x: 628,
y: 1600
}, {
x: 1420,
y: 1600
}, {
x: 1484,
y: 1600
}, {
x: 1548,
y: 1600
},
// Corner clusters
{
x: 200,
y: 300
}, {
x: 264,
y: 300
}, {
x: 200,
y: 364
}, {
x: 1784,
y: 300
}, {
x: 1848,
y: 300
}, {
x: 1784,
y: 364
}];
for (var i = 0; i < treeClusterPositions.length; i++) {
var tree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
width: 128,
height: 128
});
tree.x = treeClusterPositions[i].x;
tree.y = treeClusterPositions[i].y;
forestObjects.push(tree);
game.addChild(tree);
}
// Place bushes in organized pattern between trees and houses
var bushPositions = [
// Around left houses
{
x: 600,
y: 650
}, {
x: 600,
y: 750
}, {
x: 600,
y: 850
}, {
x: 600,
y: 950
}, {
x: 500,
y: 800
}, {
x: 500,
y: 1000
}, {
x: 500,
y: 1200
},
// Around right houses
{
x: 1450,
y: 650
}, {
x: 1450,
y: 750
}, {
x: 1450,
y: 850
}, {
x: 1450,
y: 950
}, {
x: 1550,
y: 800
}, {
x: 1550,
y: 1000
}, {
x: 1550,
y: 1200
},
// Along path edges
{
x: 900,
y: 600
}, {
x: 1150,
y: 600
}, {
x: 900,
y: 1000
}, {
x: 1150,
y: 1000
}, {
x: 900,
y: 1400
}, {
x: 1150,
y: 1400
},
// Scattered decorative bushes
{
x: 400,
y: 900
}, {
x: 1650,
y: 900
}, {
x: 400,
y: 1300
}, {
x: 1650,
y: 1300
}];
for (var i = 0; i < bushPositions.length; i++) {
var bush = LK.getAsset('bush', {
anchorX: 0.5,
anchorY: 0.5,
width: 64,
height: 64
});
bush.x = bushPositions[i].x;
bush.y = bushPositions[i].y;
forestObjects.push(bush);
game.addChild(bush);
}
isInMine = false;
}
function openMerchantInterface() {
// Simple merchant interaction - sell all items
var totalValue = 0;
var itemsSold = [];
for (var item in inventory) {
if (inventory[item] > 0) {
var itemValue = getItemValue(item);
var itemTotal = inventory[item] * itemValue;
totalValue += itemTotal;
itemsSold.push(item + ' x' + inventory[item] + ' = ' + itemTotal + ' coins');
inventory[item] = 0; // Clear inventory
}
}
if (totalValue > 0) {
LK.setScore(LK.getScore() + totalValue);
// Visual feedback
LK.effects.flashScreen(0x00FF00, 500);
}
}
function getItemValue(itemType) {
switch (itemType) {
case 'rock':
return 1;
case 'coal':
return 3;
case 'copper_ore':
return 5;
case 'iron_ore':
return 8;
case 'gold_ore':
return 15;
case 'gem':
return 25;
case 'slime':
return 2;
default:
return 1;
}
}
Heart pixart. In-Game asset. 2d. High contrast. No shadows
Muro de piedras tipo cueva. In-Game asset. 2d. High contrast. No shadows
Slime pixart cute. In-Game asset. 2d. High contrast. No shadows
Minero pixart cute. In-Game asset. 2d. High contrast. No shadows
Escalera pixart. In-Game asset. 2d. High contrast. No shadows
Gold ore poxart. In-Game asset. 2d. High contrast. No shadows
Coal ore. In-Game asset. 2d. High contrast. No shadows
Chopper ore pixart. In-Game asset. 2d. High contrast. No shadows
Rocas pixart. In-Game asset. 2d. High contrast. No shadows
Iron ore pixart. In-Game asset. 2d. High contrast. No shadows
Gema de mina super especial pixart. In-Game asset. 2d. High contrast. No shadows
flechita hacia arriba pixart. In-Game asset. 2d. High contrast. No shadows
pico de madera pixart. In-Game asset. 2d. High contrast. No shadows
espada de madera pixart. In-Game asset. 2d. High contrast. No shadows
quitale el circulo del casco
que el minero este mirando de frente y llendo de frente
suelo de tierra pixart. In-Game asset. 2d. High contrast. No shadows
boton de un inventario pixelart. In-Game asset. 2d. High contrast. No shadows
quitar la palabra "inventory"
entrada a una mina pixelart. In-Game asset. 2d. High contrast. No shadows
lobo babeando pixelart. In-Game asset. 2d. High contrast. No shadows
arbusto pixelart. In-Game asset. 2d. High contrast. No shadows
cesped pixelart. In-Game asset. 2d. High contrast. No shadows
npc market pixelart. In-Game asset. 2d. High contrast. No shadows
coin pixelart. In-Game asset. 2d. High contrast. No shadows