mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
Implement keyboard controls for player movement and interaction
- Added keyboard input handling for movement using arrow keys and WASD. - Integrated spacebar for jump functionality and 'E' key for interacting with nearest objects. - Enhanced player movement logic to prioritize keyboard input over mouse movement. - Created visual effects for player jumping, including cooldown management and overlay animations.
This commit is contained in:
@@ -17,6 +17,16 @@ export let isMoving = false;
|
||||
export let lastPlayerPosition = { x: 0, y: 0 };
|
||||
let gameRef = null;
|
||||
|
||||
// Keyboard input state
|
||||
const keyboardInput = {
|
||||
up: false,
|
||||
down: false,
|
||||
left: false,
|
||||
right: false,
|
||||
space: false
|
||||
};
|
||||
let isKeyboardMoving = false;
|
||||
|
||||
// Create player sprite
|
||||
export function createPlayer(gameInstance) {
|
||||
gameRef = gameInstance;
|
||||
@@ -63,9 +73,135 @@ export function createPlayer(gameInstance) {
|
||||
// Store player globally immediately for safety
|
||||
window.player = player;
|
||||
|
||||
// Setup keyboard input listeners
|
||||
setupKeyboardInput();
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
function setupKeyboardInput() {
|
||||
// Handle keydown events
|
||||
document.addEventListener('keydown', (event) => {
|
||||
const key = event.key.toLowerCase();
|
||||
|
||||
// Spacebar for jump
|
||||
if (key === ' ') {
|
||||
keyboardInput.space = true;
|
||||
if (window.createPlayerJump) {
|
||||
window.createPlayerJump();
|
||||
}
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// E key for interaction
|
||||
if (key === 'e') {
|
||||
if (window.tryInteractWithNearest) {
|
||||
window.tryInteractWithNearest();
|
||||
}
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys
|
||||
if (key === 'arrowup') {
|
||||
keyboardInput.up = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowdown') {
|
||||
keyboardInput.down = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowleft') {
|
||||
keyboardInput.left = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowright') {
|
||||
keyboardInput.right = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// WASD keys
|
||||
if (key === 'w') {
|
||||
keyboardInput.up = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 's') {
|
||||
keyboardInput.down = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 'a') {
|
||||
keyboardInput.left = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
} else if (key === 'd') {
|
||||
keyboardInput.right = true;
|
||||
isKeyboardMoving = true;
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Handle keyup events
|
||||
document.addEventListener('keyup', (event) => {
|
||||
const key = event.key.toLowerCase();
|
||||
|
||||
// Spacebar
|
||||
if (key === ' ') {
|
||||
keyboardInput.space = false;
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys
|
||||
if (key === 'arrowup') {
|
||||
keyboardInput.up = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowdown') {
|
||||
keyboardInput.down = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowleft') {
|
||||
keyboardInput.left = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 'arrowright') {
|
||||
keyboardInput.right = false;
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// WASD keys
|
||||
if (key === 'w') {
|
||||
keyboardInput.up = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 's') {
|
||||
keyboardInput.down = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 'a') {
|
||||
keyboardInput.left = false;
|
||||
event.preventDefault();
|
||||
} else if (key === 'd') {
|
||||
keyboardInput.right = false;
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Check if any keys are still pressed
|
||||
isKeyboardMoving = keyboardInput.up || keyboardInput.down || keyboardInput.left || keyboardInput.right;
|
||||
});
|
||||
}
|
||||
|
||||
function getAnimationKey(direction) {
|
||||
// Map left directions to their right counterparts (sprite is flipped)
|
||||
switch(direction) {
|
||||
case 'left':
|
||||
return 'right';
|
||||
case 'down-left':
|
||||
return 'down-right';
|
||||
case 'up-left':
|
||||
return 'up-right';
|
||||
default:
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
function createPlayerAnimations() {
|
||||
// Create walking animations with correct frame numbers from original
|
||||
gameRef.anims.create({
|
||||
@@ -133,6 +269,25 @@ function createPlayerAnimations() {
|
||||
frames: [{ key: 'hacker', frame: 20 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
// Create left-facing idle animations (same frames as right, but sprite will be flipped)
|
||||
gameRef.anims.create({
|
||||
key: 'idle-left',
|
||||
frames: [{ key: 'hacker', frame: 0 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
gameRef.anims.create({
|
||||
key: 'idle-down-left',
|
||||
frames: [{ key: 'hacker', frame: 20 }],
|
||||
frameRate: 1
|
||||
});
|
||||
|
||||
gameRef.anims.create({
|
||||
key: 'idle-up-left',
|
||||
frames: [{ key: 'hacker', frame: 15 }],
|
||||
frameRate: 1
|
||||
});
|
||||
}
|
||||
|
||||
export function movePlayerToPoint(x, y) {
|
||||
@@ -194,6 +349,118 @@ export function updatePlayerMovement() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle keyboard movement (takes priority over mouse movement)
|
||||
if (isKeyboardMoving) {
|
||||
updatePlayerKeyboardMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle mouse-based movement (original behavior)
|
||||
updatePlayerMouseMovement();
|
||||
}
|
||||
|
||||
function updatePlayerKeyboardMovement() {
|
||||
// Calculate movement direction based on keyboard input
|
||||
let dirX = 0;
|
||||
let dirY = 0;
|
||||
|
||||
if (keyboardInput.right) dirX += 1;
|
||||
if (keyboardInput.left) dirX -= 1;
|
||||
if (keyboardInput.down) dirY += 1;
|
||||
if (keyboardInput.up) dirY -= 1;
|
||||
|
||||
// Normalize diagonal movement to maintain consistent speed
|
||||
let velocityX = 0;
|
||||
let velocityY = 0;
|
||||
|
||||
if (dirX !== 0 || dirY !== 0) {
|
||||
const magnitude = Math.sqrt(dirX * dirX + dirY * dirY);
|
||||
velocityX = (dirX / magnitude) * MOVEMENT_SPEED;
|
||||
velocityY = (dirY / magnitude) * MOVEMENT_SPEED;
|
||||
}
|
||||
|
||||
// Check if movement is being blocked by collisions
|
||||
let isBlocked = false;
|
||||
if (velocityX !== 0 || velocityY !== 0) {
|
||||
// Check if blocked in the direction we want to move
|
||||
if (velocityX > 0 && player.body.blocked.right) isBlocked = true;
|
||||
if (velocityX < 0 && player.body.blocked.left) isBlocked = true;
|
||||
if (velocityY > 0 && player.body.blocked.down) isBlocked = true;
|
||||
if (velocityY < 0 && player.body.blocked.up) isBlocked = true;
|
||||
}
|
||||
|
||||
// Apply velocity
|
||||
player.body.setVelocity(velocityX, velocityY);
|
||||
|
||||
// Update player depth based on actual player position
|
||||
updatePlayerDepth(player.x, player.y);
|
||||
|
||||
// Update last player position for depth calculations
|
||||
lastPlayerPosition.x = player.x;
|
||||
lastPlayerPosition.y = player.y;
|
||||
|
||||
// Determine direction based on velocity
|
||||
const absVX = Math.abs(velocityX);
|
||||
const absVY = Math.abs(velocityY);
|
||||
|
||||
// Set player direction and animation
|
||||
if (velocityX === 0 && velocityY === 0) {
|
||||
// No movement - stop
|
||||
if (player.isMoving) {
|
||||
player.isMoving = false;
|
||||
const animDir = getAnimationKey(player.direction);
|
||||
player.anims.stop(); // Stop current animation
|
||||
player.anims.play(`idle-${animDir}`, true);
|
||||
}
|
||||
} else if (isBlocked) {
|
||||
// Blocked by collision - play idle animation in the direction we're facing
|
||||
if (player.isMoving) {
|
||||
player.isMoving = false;
|
||||
const animDir = getAnimationKey(player.direction);
|
||||
player.anims.stop(); // Stop current animation
|
||||
player.anims.play(`idle-${animDir}`, true);
|
||||
}
|
||||
} else if (absVX > absVY * 2) {
|
||||
// Mostly horizontal movement
|
||||
player.direction = velocityX > 0 ? 'right' : 'left'; // Track both left and right directions
|
||||
player.setFlipX(velocityX < 0); // Flip sprite horizontally if moving left
|
||||
|
||||
if (!player.isMoving || player.lastDirection !== player.direction) {
|
||||
// Use 'right' animation for both left and right (flip handled by setFlipX)
|
||||
player.anims.play(`walk-right`, true);
|
||||
player.isMoving = true;
|
||||
player.lastDirection = player.direction;
|
||||
}
|
||||
} else if (absVY > absVX * 2) {
|
||||
// Mostly vertical movement
|
||||
player.direction = velocityY > 0 ? 'down' : 'up';
|
||||
player.setFlipX(false);
|
||||
|
||||
if (!player.isMoving || player.lastDirection !== player.direction) {
|
||||
player.anims.play(`walk-${player.direction}`, true);
|
||||
player.isMoving = true;
|
||||
player.lastDirection = player.direction;
|
||||
}
|
||||
} else {
|
||||
// Diagonal movement
|
||||
if (velocityY > 0) {
|
||||
player.direction = velocityX > 0 ? 'down-right' : 'down-left';
|
||||
} else {
|
||||
player.direction = velocityX > 0 ? 'up-right' : 'up-left';
|
||||
}
|
||||
player.setFlipX(velocityX < 0); // Flip sprite horizontally if moving left
|
||||
|
||||
if (!player.isMoving || player.lastDirection !== player.direction) {
|
||||
// Use the base direction for animation (right or left for horizontal component)
|
||||
const baseDir = velocityY > 0 ? 'down-right' : 'up-right';
|
||||
player.anims.play(`walk-${baseDir}`, true);
|
||||
player.isMoving = true;
|
||||
player.lastDirection = player.direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePlayerMouseMovement() {
|
||||
if (!isMoving || !targetPoint) {
|
||||
if (player.body.velocity.x !== 0 || player.body.velocity.y !== 0) {
|
||||
player.body.setVelocity(0, 0);
|
||||
@@ -221,10 +488,12 @@ export function updatePlayerMovement() {
|
||||
if (distanceSq < ARRIVAL_THRESHOLD * ARRIVAL_THRESHOLD) {
|
||||
isMoving = false;
|
||||
player.body.setVelocity(0, 0);
|
||||
player.isMoving = false;
|
||||
|
||||
// Play idle animation based on last direction
|
||||
player.anims.play(`idle-${player.direction}`, true);
|
||||
if (player.isMoving) {
|
||||
player.isMoving = false;
|
||||
const animDir = getAnimationKey(player.direction);
|
||||
player.anims.stop(); // Stop current animation
|
||||
player.anims.play(`idle-${animDir}`, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,8 +543,12 @@ export function updatePlayerMovement() {
|
||||
if (player.body.blocked.none === false) {
|
||||
isMoving = false;
|
||||
player.body.setVelocity(0, 0);
|
||||
player.isMoving = false;
|
||||
player.anims.play(`idle-${player.direction}`, true);
|
||||
if (player.isMoving) {
|
||||
player.isMoving = false;
|
||||
const animDir = getAnimationKey(player.direction);
|
||||
player.anims.stop(); // Stop current animation
|
||||
player.anims.play(`idle-${animDir}`, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -875,6 +875,7 @@ window.checkDoorTransitions = checkDoorTransitions;
|
||||
window.setupDoorOverlapChecks = setupDoorOverlapChecks;
|
||||
window.updateDoorZoneVisibility = updateDoorZoneVisibility;
|
||||
window.processAllDoorCollisions = processAllDoorCollisions;
|
||||
window.handleDoorInteraction = handleDoorInteraction;
|
||||
|
||||
// Export functions for use by other modules
|
||||
export { unlockDoor };
|
||||
export { unlockDoor, handleDoorInteraction };
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { INTERACTION_RANGE, INTERACTION_RANGE_SQ, INTERACTION_CHECK_INTERVAL } from '../utils/constants.js?v=7';
|
||||
import { rooms } from '../core/rooms.js?v=16';
|
||||
import { handleUnlock } from './unlock-system.js';
|
||||
import { handleDoorInteraction } from './doors.js';
|
||||
import { collectFingerprint, handleBiometricScan } from './biometrics.js';
|
||||
import { addToInventory, removeFromInventory, createItemIdentifier } from './inventory.js';
|
||||
|
||||
@@ -562,7 +563,136 @@ function handleContainerInteraction(sprite) {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to interact with the nearest interactable object within range
|
||||
export function tryInteractWithNearest() {
|
||||
const player = window.player;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
const px = player.x;
|
||||
const py = player.y;
|
||||
|
||||
let nearestObject = null;
|
||||
let nearestDistance = INTERACTION_RANGE; // Only consider objects within interaction range
|
||||
|
||||
// Get player's facing direction and convert to angle for direction filtering
|
||||
const playerDirection = player.direction || 'down';
|
||||
let facingAngle = 0;
|
||||
let angleTolerance = 70; // degrees - how wide the cone in front of player is
|
||||
|
||||
// Determine facing angle based on direction
|
||||
// In canvas/Phaser: right=0°, down=90°, left=180°, up=270°
|
||||
switch(playerDirection) {
|
||||
case 'right':
|
||||
facingAngle = 0;
|
||||
break;
|
||||
case 'down-right':
|
||||
facingAngle = 45;
|
||||
break;
|
||||
case 'down':
|
||||
facingAngle = 90;
|
||||
break;
|
||||
case 'down-left':
|
||||
facingAngle = 135;
|
||||
break;
|
||||
case 'left':
|
||||
facingAngle = 180;
|
||||
break;
|
||||
case 'up-left':
|
||||
facingAngle = 225;
|
||||
break;
|
||||
case 'up':
|
||||
facingAngle = 270; // Use 270 instead of -90
|
||||
break;
|
||||
case 'up-right':
|
||||
facingAngle = 315; // Use 315 instead of -45
|
||||
break;
|
||||
default: // Fallback for any unknown directions
|
||||
facingAngle = 90; // Default to down
|
||||
}
|
||||
|
||||
// Helper function to check if an object is in front of the player
|
||||
function isInFrontOfPlayer(objX, objY) {
|
||||
const dx = objX - px;
|
||||
const dy = objY - py;
|
||||
|
||||
// Calculate angle to object (in canvas coordinates where Y increases downward)
|
||||
let angleToObject = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||
|
||||
// Normalize to 0-360
|
||||
angleToObject = (angleToObject + 360) % 360;
|
||||
|
||||
// Calculate angular difference
|
||||
let angleDiff = Math.abs(facingAngle - angleToObject);
|
||||
if (angleDiff > 180) {
|
||||
angleDiff = 360 - angleDiff;
|
||||
}
|
||||
|
||||
return angleDiff <= angleTolerance;
|
||||
}
|
||||
|
||||
// Check all objects in all rooms
|
||||
Object.entries(rooms).forEach(([roomId, room]) => {
|
||||
if (!room.objects) return;
|
||||
|
||||
Object.values(room.objects).forEach(obj => {
|
||||
// Only consider interactable, active, and visible objects
|
||||
if (!obj.active || !obj.interactable || !obj.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dx = px - obj.x;
|
||||
const dy = py - obj.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Check if within range and in front of player
|
||||
if (distance <= INTERACTION_RANGE && isInFrontOfPlayer(obj.x, obj.y)) {
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestObject = obj;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Also check door sprites (including all doors, not just locked ones)
|
||||
if (room.doorSprites) {
|
||||
Object.values(room.doorSprites).forEach(door => {
|
||||
// Only consider active doors (check all doors, not just locked)
|
||||
if (!door.active || !door.doorProperties) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dx = px - door.x;
|
||||
const dy = py - door.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Check if within range and in front of player
|
||||
if (distance <= INTERACTION_RANGE && isInFrontOfPlayer(door.x, door.y)) {
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestObject = door;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Interact with the nearest object if one was found
|
||||
if (nearestObject) {
|
||||
// Check if this is a door (doors have doorProperties instead of scenarioData)
|
||||
if (nearestObject.doorProperties) {
|
||||
// Handle door interaction - triggers unlock/open sequence based on lock state
|
||||
handleDoorInteraction(nearestObject);
|
||||
} else {
|
||||
// Handle regular object interaction
|
||||
handleObjectInteraction(nearestObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export for global access
|
||||
window.checkObjectInteractions = checkObjectInteractions;
|
||||
window.handleObjectInteraction = handleObjectInteraction;
|
||||
window.handleContainerInteraction = handleContainerInteraction;
|
||||
window.tryInteractWithNearest = tryInteractWithNearest;
|
||||
|
||||
@@ -18,7 +18,9 @@ let lastPlayerPosition = { x: 0, y: 0 };
|
||||
let steppedOverItems = new Set(); // Track items we've already stepped over
|
||||
let playerVisualOverlay = null; // Visual overlay for hop effect
|
||||
let lastHopTime = 0; // Track when last hop occurred
|
||||
let lastJumpTime = 0; // Track when last jump occurred
|
||||
const HOP_COOLDOWN = 300; // 300ms cooldown between hops
|
||||
const JUMP_COOLDOWN = 600; // 600ms cooldown between jumps
|
||||
|
||||
// Initialize player effects system
|
||||
export function initializePlayerEffects(gameInstance, roomsRef) {
|
||||
@@ -188,6 +190,99 @@ export function createPlayerBumpEffect() {
|
||||
});
|
||||
}
|
||||
|
||||
// Create player jump effect when spacebar is pressed
|
||||
export function createPlayerJump() {
|
||||
if (!window.player || isPlayerBumping) return;
|
||||
|
||||
// Check cooldown to prevent rapid jumping
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - lastJumpTime < JUMP_COOLDOWN) {
|
||||
return; // Still in cooldown, skip this jump
|
||||
}
|
||||
|
||||
const player = window.player;
|
||||
|
||||
// Update jump time
|
||||
lastJumpTime = currentTime;
|
||||
isPlayerBumping = true;
|
||||
|
||||
// Create hop effect using visual overlay (same as bump effect)
|
||||
if (playerBumpTween) {
|
||||
playerBumpTween.destroy();
|
||||
}
|
||||
|
||||
// Create a visual overlay sprite that follows the player
|
||||
if (playerVisualOverlay) {
|
||||
playerVisualOverlay.destroy();
|
||||
}
|
||||
|
||||
playerVisualOverlay = gameRef.add.sprite(player.x, player.y, player.texture.key);
|
||||
playerVisualOverlay.setFrame(player.frame.name);
|
||||
playerVisualOverlay.setScale(player.scaleX, player.scaleY);
|
||||
playerVisualOverlay.setFlipX(player.flipX); // Copy horizontal flip state
|
||||
playerVisualOverlay.setFlipY(player.flipY); // Copy vertical flip state
|
||||
playerVisualOverlay.setDepth(player.depth + 1);
|
||||
playerVisualOverlay.setAlpha(0.8);
|
||||
|
||||
// Hide the original player temporarily
|
||||
player.setAlpha(0);
|
||||
|
||||
// Jump upward - negative Y values move sprite up on screen
|
||||
const jumpHeight = -20; // Consistent upward jump
|
||||
|
||||
// Debug: Log the jump details
|
||||
console.log(`Jump triggered - Player Y: ${player.y}, Overlay Y: ${playerVisualOverlay.y}, Jump Height: ${jumpHeight}, Target Y: ${playerVisualOverlay.y + jumpHeight}`);
|
||||
|
||||
// Start the jump animation with a simple up-down motion
|
||||
playerBumpTween = gameRef.tweens.add({
|
||||
targets: { jumpOffset: 0 },
|
||||
jumpOffset: jumpHeight,
|
||||
duration: 150,
|
||||
ease: 'Power2',
|
||||
yoyo: true,
|
||||
onUpdate: (tween) => {
|
||||
if (playerVisualOverlay && playerVisualOverlay.active) {
|
||||
// Apply the jump offset to the current player position
|
||||
playerVisualOverlay.setY(player.y + tween.getValue());
|
||||
}
|
||||
},
|
||||
onComplete: () => {
|
||||
// Clean up overlay and restore player
|
||||
if (playerVisualOverlay) {
|
||||
playerVisualOverlay.destroy();
|
||||
playerVisualOverlay = null;
|
||||
}
|
||||
player.setAlpha(1); // Restore player visibility
|
||||
isPlayerBumping = false;
|
||||
playerBumpTween = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Make overlay follow player movement during jump
|
||||
const followPlayer = () => {
|
||||
if (playerVisualOverlay && playerVisualOverlay.active) {
|
||||
// Update X position and flip states, Y is handled by the tween
|
||||
playerVisualOverlay.setX(player.x);
|
||||
playerVisualOverlay.setFlipX(player.flipX); // Update flip state
|
||||
playerVisualOverlay.setFlipY(player.flipY); // Update flip state
|
||||
}
|
||||
};
|
||||
|
||||
// Update overlay position every frame during jump
|
||||
const followInterval = setInterval(() => {
|
||||
if (!playerVisualOverlay || !playerVisualOverlay.active) {
|
||||
clearInterval(followInterval);
|
||||
return;
|
||||
}
|
||||
followPlayer();
|
||||
}, 16); // ~60fps
|
||||
|
||||
// Clean up interval when jump completes
|
||||
setTimeout(() => {
|
||||
clearInterval(followInterval);
|
||||
}, 280); // Slightly longer than animation duration
|
||||
}
|
||||
|
||||
// Create plant animation effect when player bumps into animated plants
|
||||
export function createPlantBumpEffect() {
|
||||
if (!window.player) return;
|
||||
@@ -231,4 +326,5 @@ export function createPlantBumpEffect() {
|
||||
|
||||
// Export for global access
|
||||
window.createPlayerBumpEffect = createPlayerBumpEffect;
|
||||
window.createPlayerJump = createPlayerJump;
|
||||
window.createPlantBumpEffect = createPlantBumpEffect;
|
||||
|
||||
Reference in New Issue
Block a user