about summary refs log tree commit diff stats
path: root/html/rogue/js/player.js
blob: e35db315c6d7c52f4b71bc5ec7b934a4148a9db7 (plain) (blame)
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();
        }
    }
};