// Player state and controls
const player = {
position: { q: 0, r: 0 }, // Current hex position
target: null, // Target hex to move to
path: [], // Array of hex coordinates to follow
movementProgress: 0, // Progress of current movement (0 to 1)
moveSpeed: Config.player.MOVE_SPEED, // Movement speed (0 to 1 per frame)
inventory: [],
// Initialize player
init() {
this.position = { q: 0, r: 0 };
this.target = null;
this.path = [];
this.inventory = [];
return this;
},
// Check if a hex coordinate is within grid bounds
isValidHex(hex) {
const halfGrid = Math.floor(HexGrid.GRID_SIZE / 2);
return Math.abs(hex.q) <= halfGrid && Math.abs(hex.r) <= halfGrid;
},
// Get neighbors that share an edge with the given hex
getEdgeNeighbors(hex) {
const directions = [
{q: 1, r: 0}, // East
{q: 0, r: 1}, // Southeast
{q: -1, r: 1}, // Southwest
{q: -1, r: 0}, // West
{q: 0, r: -1}, // Northwest
{q: 1, r: -1} // Northeast
];
// Only return neighbors that are within grid bounds
return directions
.map(dir => ({
q: hex.q + dir.q,
r: hex.r + dir.r
}))
.filter(hex => this.isValidHex(hex));
},
// Find path from current position to target
findPath(targetHex) {
const start = this.position;
const goal = targetHex;
// Simple breadth-first search
const queue = [[start]];
const visited = new Set();
const key = hex => `${hex.q},${hex.r}`;
visited.add(key(start));
while (queue.length > 0) {
const path = queue.shift();
const current = path[path.length - 1];
if (current.q === goal.q && current.r === goal.r) {
return path;
}
const neighbors = this.getEdgeNeighbors(current);
for (const neighbor of neighbors) {
const neighborKey = key(neighbor);
if (!visited.has(neighborKey)) {
visited.add(neighborKey);
queue.push([...path, neighbor]);
}
}
}
return null; // No path found
},
// Start moving to a target hex
moveTo(targetHex) {
// Only start new movement if we're not already moving and target is valid
if (!this.target) {
// Check if target is within grid bounds
if (!this.isValidHex(targetHex)) {
return; // Ignore movement request if target is out of bounds
}
const path = this.findPath(targetHex);
if (path) {
// Filter out any path points that would go out of bounds
this.path = path.slice(1).filter(hex => this.isValidHex(hex));
if (this.path.length > 0) {
this.target = this.path.shift();
this.movementProgress = 0;
}
}
}
},
// Add item to inventory
addToInventory(item) {
this.inventory.push(item);
},
// Check for and collect items
checkForItems() {
const item = Items.getItem(this.position.q, this.position.r);
if (item) {
Items.removeItem(this.position.q, this.position.r);
this.addToInventory(item);
}
},
// Update player position
update() {
if (this.target) {
this.movementProgress += this.moveSpeed;
if (this.movementProgress >= 1) {
this.position = this.target;
this.target = null;
this.movementProgress = 0;
this.hasMoved = true;
// Check for items when reaching new position
this.checkForItems();
if (this.path.length > 0) {
this.target = this.path.shift();
this.movementProgress = 0;
}
}
}
},
// Get current interpolated position
getCurrentPosition() {
if (!this.target) {
return this.position;
}
// Interpolate between current position and target
return {
q: this.position.q + (this.target.q - this.position.q) * this.movementProgress,
r: this.position.r + (this.target.r - this.position.r) * this.movementProgress
};
},
// Draw the player
draw(ctx, hexToPixel, camera, HEX_SIZE) {
const currentPos = this.getCurrentPosition();
const pixelPos = hexToPixel(currentPos);
const screenX = pixelPos.x - camera.x;
const screenY = pixelPos.y - camera.y;
ctx.fillStyle = Config.colors.PLAYER;
ctx.beginPath();
ctx.arc(screenX, screenY, HEX_SIZE * Config.player.SIZE_RATIO, 0, Math.PI * 2);
ctx.fill();
// Draw path if needed
if (this.path.length > 0) {
ctx.strokeStyle = Config.colors.PLAYER + '4D'; // 30% opacity version of player color
ctx.beginPath();
let lastPos = this.target || this.position;
this.path.forEach(point => {
const from = hexToPixel(lastPos);
const to = hexToPixel(point);
ctx.moveTo(from.x - camera.x, from.y - camera.y);
ctx.lineTo(to.x - camera.x, to.y - camera.y);
lastPos = point;
});
ctx.stroke();
}
}
};