about summary refs log tree commit diff stats
path: root/html/rogue/js/player.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/rogue/js/player.js')
-rw-r--r--html/rogue/js/player.js241
1 files changed, 241 insertions, 0 deletions
diff --git a/html/rogue/js/player.js b/html/rogue/js/player.js
new file mode 100644
index 0000000..efecbdf
--- /dev/null
+++ b/html/rogue/js/player.js
@@ -0,0 +1,241 @@
+// 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: [],
+    
+    // Animation properties
+    animation: null,
+    sprites: [],
+    
+    // Initialize player
+    async init() {
+        this.position = { q: 0, r: 0 };
+        this.target = null;
+        this.path = [];
+        this.inventory = [];
+        
+        // Load sprites
+        try {
+            const [sprite1, sprite2] = await Promise.all([
+                Animation.loadImage('assets/home/goblin-01.png'),
+                Animation.loadImage('assets/home/goblin-02.png')
+            ]);
+            
+            this.sprites = [sprite1, sprite2];
+            this.animation = Animation.createAnimation(this.sprites, 500); // 500ms per frame
+        } catch (error) {
+            console.error('Failed to load player sprites:', error);
+        }
+        
+        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;
+
+        if (this.animation && this.sprites.length > 0) {
+            // Get current sprite from animation
+            const currentSprite = this.animation.update(performance.now());
+            
+            // Scale sprite to fit within hex
+            // Use slightly smaller than hex size to ensure it fits visually
+            const hexInnerSize = HEX_SIZE * 0.8; // 80% of hex size
+            const { width, height, scale } = Animation.scaleToFit(
+                currentSprite, 
+                hexInnerSize * 2, // width
+                hexInnerSize * Math.sqrt(3) // height (hex height)
+            );
+            
+            // Calculate position to center the sprite in the hex
+            const spriteX = screenX - width / 2;
+            const spriteY = screenY - height / 2;
+            
+            // Save context state
+            ctx.save();
+            
+            // Optional: add a small bounce effect when moving
+            if (this.target) {
+                const bounce = Math.sin(performance.now() / 100) * 2;
+                ctx.translate(spriteX, spriteY + bounce);
+            } else {
+                ctx.translate(spriteX, spriteY);
+            }
+            
+            // Draw the sprite
+            ctx.drawImage(
+                currentSprite,
+                0, 0,
+                width,
+                height
+            );
+            
+            // Restore context state
+            ctx.restore();
+            
+            // Debug: draw hex bounds if debug is enabled
+            if (Debug.isEnabled) {
+                ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
+                ctx.beginPath();
+                HexGrid.drawHexPath(ctx, screenX, screenY, HEX_SIZE * 0.8);
+                ctx.stroke();
+            }
+        } else {
+            // Fallback to circle if sprites aren't loaded
+            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';
+            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();
+        }
+    }
+}; 
\ No newline at end of file