feat: Add block breaking, HUD, and inventory system for player
This commit is contained in:
@@ -25,6 +25,26 @@ class Player extends Entity {
|
||||
this.lastDirection = 1; // Track last direction to prevent unnecessary flipping
|
||||
this.isClimbing = false; // Track climbing state
|
||||
|
||||
// Player stats
|
||||
this.maxHealth = 100;
|
||||
this.health = 100;
|
||||
this.breakingPower = 1;
|
||||
this.breakingRange = 3;
|
||||
this.isBreaking = false;
|
||||
this.breakingCooldown = 0;
|
||||
this.breakingCooldownMax = 10;
|
||||
|
||||
// Inventory
|
||||
this.inventory = {
|
||||
sand: 0,
|
||||
water: 0,
|
||||
dirt: 0,
|
||||
stone: 0,
|
||||
wood: 0,
|
||||
grass: 0,
|
||||
seed: 0
|
||||
};
|
||||
|
||||
// Animation properties
|
||||
this.frameWidth = 32;
|
||||
this.frameHeight = 30;
|
||||
@@ -87,12 +107,26 @@ class Player extends Entity {
|
||||
this.x = newX;
|
||||
this.y = newY;
|
||||
|
||||
// Update breaking cooldown
|
||||
if (this.breakingCooldown > 0) {
|
||||
this.breakingCooldown--;
|
||||
}
|
||||
|
||||
// Handle breaking action
|
||||
if (this.isBreaking && this.breakingCooldown <= 0) {
|
||||
this.breakBlock();
|
||||
this.breakingCooldown = this.breakingCooldownMax;
|
||||
}
|
||||
|
||||
// Update animation
|
||||
this.updateAnimation(deltaTime);
|
||||
|
||||
// Center camera on player
|
||||
this.centerCamera();
|
||||
|
||||
// Update HUD
|
||||
this.updateHUD();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -209,6 +243,224 @@ class Player extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
startBreaking() {
|
||||
this.isBreaking = true;
|
||||
}
|
||||
|
||||
stopBreaking() {
|
||||
this.isBreaking = false;
|
||||
}
|
||||
|
||||
breakBlock() {
|
||||
// Calculate the position in front of the player
|
||||
const halfWidth = this.width / 2;
|
||||
const breakX = Math.floor(this.x + (this.direction * this.breakingRange));
|
||||
const breakY = Math.floor(this.y);
|
||||
|
||||
// Get the block type at that position
|
||||
const blockType = getPixel(breakX, breakY);
|
||||
|
||||
// Only break non-empty blocks that aren't special entities
|
||||
if (blockType !== EMPTY &&
|
||||
blockType !== WATER &&
|
||||
blockType !== FIRE &&
|
||||
blockType !== SQUARE &&
|
||||
blockType !== CIRCLE &&
|
||||
blockType !== TRIANGLE) {
|
||||
|
||||
// Add to inventory based on block type
|
||||
this.addToInventory(blockType);
|
||||
|
||||
// Replace with empty space
|
||||
setPixel(breakX, breakY, EMPTY);
|
||||
|
||||
// Create a breaking effect (particles)
|
||||
this.createBreakingEffect(breakX, breakY, blockType);
|
||||
}
|
||||
}
|
||||
|
||||
addToInventory(blockType) {
|
||||
// Map block type to inventory item
|
||||
switch(blockType) {
|
||||
case SAND:
|
||||
this.inventory.sand++;
|
||||
break;
|
||||
case DIRT:
|
||||
this.inventory.dirt++;
|
||||
break;
|
||||
case STONE:
|
||||
this.inventory.stone++;
|
||||
break;
|
||||
case GRASS:
|
||||
this.inventory.grass++;
|
||||
break;
|
||||
case WOOD:
|
||||
this.inventory.wood++;
|
||||
break;
|
||||
case SEED:
|
||||
case TREE_SEED:
|
||||
this.inventory.seed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
createBreakingEffect(x, y, blockType) {
|
||||
// Create a simple particle effect at the breaking location
|
||||
// This could be expanded with a proper particle system
|
||||
const numParticles = 5;
|
||||
|
||||
// For now, we'll just create a visual feedback by setting nearby pixels
|
||||
// to a different color briefly, then clearing them
|
||||
for (let dy = -1; dy <= 1; dy++) {
|
||||
for (let dx = -1; dx <= 1; dx++) {
|
||||
if (dx === 0 && dy === 0) continue;
|
||||
|
||||
// Skip if the pixel is not empty
|
||||
if (getPixel(x + dx, y + dy) !== EMPTY) continue;
|
||||
|
||||
// Set a temporary pixel
|
||||
setPixel(x + dx, y + dy, EMPTY);
|
||||
|
||||
// Mark the chunk as dirty for rendering
|
||||
const { chunkX, chunkY } = getChunkCoordinates(x + dx, y + dy);
|
||||
const key = getChunkKey(chunkX, chunkY);
|
||||
dirtyChunks.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHUD() {
|
||||
// Get or create the HUD container
|
||||
let hudContainer = document.getElementById('player-hud');
|
||||
if (!hudContainer) {
|
||||
hudContainer = document.createElement('div');
|
||||
hudContainer.id = 'player-hud';
|
||||
hudContainer.style.position = 'fixed';
|
||||
hudContainer.style.bottom = '10px';
|
||||
hudContainer.style.left = '10px';
|
||||
hudContainer.style.width = '300px';
|
||||
hudContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
|
||||
hudContainer.style.color = 'white';
|
||||
hudContainer.style.padding = '10px';
|
||||
hudContainer.style.borderRadius = '5px';
|
||||
hudContainer.style.fontFamily = 'Arial, sans-serif';
|
||||
hudContainer.style.zIndex = '1000';
|
||||
document.body.appendChild(hudContainer);
|
||||
|
||||
// Create health bar container
|
||||
const healthBarContainer = document.createElement('div');
|
||||
healthBarContainer.id = 'health-bar-container';
|
||||
healthBarContainer.style.width = '100%';
|
||||
healthBarContainer.style.height = '20px';
|
||||
healthBarContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.3)';
|
||||
healthBarContainer.style.marginBottom = '10px';
|
||||
healthBarContainer.style.borderRadius = '3px';
|
||||
|
||||
// Create health bar
|
||||
const healthBar = document.createElement('div');
|
||||
healthBar.id = 'health-bar';
|
||||
healthBar.style.width = '100%';
|
||||
healthBar.style.height = '100%';
|
||||
healthBar.style.backgroundColor = '#4CAF50';
|
||||
healthBar.style.borderRadius = '3px';
|
||||
healthBar.style.transition = 'width 0.3s';
|
||||
|
||||
healthBarContainer.appendChild(healthBar);
|
||||
hudContainer.appendChild(healthBarContainer);
|
||||
|
||||
// Create inventory container
|
||||
const inventoryContainer = document.createElement('div');
|
||||
inventoryContainer.id = 'inventory-container';
|
||||
inventoryContainer.style.display = 'grid';
|
||||
inventoryContainer.style.gridTemplateColumns = 'repeat(7, 1fr)';
|
||||
inventoryContainer.style.gap = '5px';
|
||||
|
||||
// Create inventory slots
|
||||
const inventoryItems = ['sand', 'dirt', 'stone', 'grass', 'wood', 'water', 'seed'];
|
||||
inventoryItems.forEach(item => {
|
||||
const slot = document.createElement('div');
|
||||
slot.id = `inventory-${item}`;
|
||||
slot.className = 'inventory-slot';
|
||||
slot.style.width = '30px';
|
||||
slot.style.height = '30px';
|
||||
slot.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
|
||||
slot.style.borderRadius = '3px';
|
||||
slot.style.display = 'flex';
|
||||
slot.style.flexDirection = 'column';
|
||||
slot.style.alignItems = 'center';
|
||||
slot.style.justifyContent = 'center';
|
||||
slot.style.fontSize = '10px';
|
||||
slot.style.position = 'relative';
|
||||
|
||||
// Create item icon
|
||||
const icon = document.createElement('div');
|
||||
icon.style.width = '20px';
|
||||
icon.style.height = '20px';
|
||||
icon.style.borderRadius = '3px';
|
||||
|
||||
// Set color based on item type
|
||||
switch(item) {
|
||||
case 'sand': icon.style.backgroundColor = '#e6c588'; break;
|
||||
case 'dirt': icon.style.backgroundColor = '#8B4513'; break;
|
||||
case 'stone': icon.style.backgroundColor = '#A9A9A9'; break;
|
||||
case 'grass': icon.style.backgroundColor = '#7CFC00'; break;
|
||||
case 'wood': icon.style.backgroundColor = '#8B5A2B'; break;
|
||||
case 'water': icon.style.backgroundColor = '#4a80f5'; break;
|
||||
case 'seed': icon.style.backgroundColor = '#654321'; break;
|
||||
}
|
||||
|
||||
// Create count label
|
||||
const count = document.createElement('div');
|
||||
count.id = `${item}-count`;
|
||||
count.style.position = 'absolute';
|
||||
count.style.bottom = '2px';
|
||||
count.style.right = '2px';
|
||||
count.style.fontSize = '8px';
|
||||
count.style.fontWeight = 'bold';
|
||||
count.textContent = '0';
|
||||
|
||||
slot.appendChild(icon);
|
||||
slot.appendChild(count);
|
||||
inventoryContainer.appendChild(slot);
|
||||
});
|
||||
|
||||
hudContainer.appendChild(inventoryContainer);
|
||||
|
||||
// Create controls help text
|
||||
const controlsHelp = document.createElement('div');
|
||||
controlsHelp.style.marginTop = '10px';
|
||||
controlsHelp.style.fontSize = '10px';
|
||||
controlsHelp.style.color = '#aaa';
|
||||
controlsHelp.innerHTML = 'Controls: A/D - Move, W/Space - Jump, E - Break blocks';
|
||||
|
||||
hudContainer.appendChild(controlsHelp);
|
||||
}
|
||||
|
||||
// Update health bar
|
||||
const healthBar = document.getElementById('health-bar');
|
||||
if (healthBar) {
|
||||
const healthPercent = (this.health / this.maxHealth) * 100;
|
||||
healthBar.style.width = `${healthPercent}%`;
|
||||
|
||||
// Change color based on health
|
||||
if (healthPercent > 60) {
|
||||
healthBar.style.backgroundColor = '#4CAF50'; // Green
|
||||
} else if (healthPercent > 30) {
|
||||
healthBar.style.backgroundColor = '#FFC107'; // Yellow
|
||||
} else {
|
||||
healthBar.style.backgroundColor = '#F44336'; // Red
|
||||
}
|
||||
}
|
||||
|
||||
// Update inventory counts
|
||||
for (const [item, count] of Object.entries(this.inventory)) {
|
||||
const countElement = document.getElementById(`${item}-count`);
|
||||
if (countElement) {
|
||||
countElement.textContent = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to climb up a small step
|
||||
tryClimbing(newX, newY) {
|
||||
const halfWidth = this.width / 2;
|
||||
|
||||
Reference in New Issue
Block a user