1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
// 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)
// Initialize player
init() {
this.position = { q: 0, r: 0 };
this.target = null;
this.path = [];
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;
}
}
}
},
// Update player position
update() {
if (this.target) {
this.movementProgress += this.moveSpeed;
if (this.movementProgress >= 1) {
// Movement to current target complete
this.position = this.target;
this.target = null;
this.movementProgress = 0;
// If there are more points in the path, move to the next one
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();
// Optionally, draw the remaining path
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();
}
}
};
|