mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
Enhance player combat animations and effects with punch mechanics
This commit is contained in:
@@ -374,6 +374,7 @@ function createAtlasPlayerAnimations(spriteSheet) {
|
||||
const playerConfig = window.gameScenario?.player?.spriteConfig || {};
|
||||
const idleFrameRate = playerConfig.idleFrameRate || 6; // Slower for breathing effect
|
||||
const walkFrameRate = playerConfig.walkFrameRate || 10;
|
||||
const punchFrameRate = playerConfig.punchFrameRate || 12; // Faster for action animations
|
||||
|
||||
// Direction mapping: atlas directions → player directions
|
||||
const directionMap = {
|
||||
@@ -392,6 +393,14 @@ function createAtlasPlayerAnimations(spriteSheet) {
|
||||
'breathing-idle': 'idle',
|
||||
'walk': 'walk'
|
||||
};
|
||||
|
||||
// Animation type framework (for grouping and frame rate)
|
||||
const animationFramework = {
|
||||
'idle': { frameRate: idleFrameRate, repeat: -1, name: 'idle' },
|
||||
'walk': { frameRate: walkFrameRate, repeat: -1, name: 'walk' },
|
||||
'cross-punch': { frameRate: punchFrameRate, repeat: 0, name: 'attack' },
|
||||
'lead-jab': { frameRate: punchFrameRate, repeat: 0, name: 'attack' }
|
||||
};
|
||||
|
||||
// Create animations from atlas metadata
|
||||
for (const [atlasAnimKey, frames] of Object.entries(animations)) {
|
||||
@@ -404,8 +413,15 @@ function createAtlasPlayerAnimations(spriteSheet) {
|
||||
const playerDirection = directionMap[atlasDirection] || atlasDirection;
|
||||
const playerType = animTypeMap[atlasType] || atlasType;
|
||||
|
||||
// Create animation key: "walk-right", "idle-down", etc.
|
||||
const animKey = `${playerType}-${playerDirection}`;
|
||||
// Create animation key: "walk-right", "idle-down", "cross-punch_east", "lead-jab_south", etc.
|
||||
const animKey = playerType === 'idle' || playerType === 'walk'
|
||||
? `${playerType}-${playerDirection}`
|
||||
: `${playerType}_${atlasDirection}`; // Keep atlas direction for punch animations
|
||||
|
||||
// Debug for punch animations
|
||||
if (playerType === 'cross-punch' || playerType === 'lead-jab') {
|
||||
console.log(` - Punch anim: ${atlasAnimKey} → type: ${playerType}, direction: ${atlasDirection}, key: ${animKey}, frames: ${frames.length}`);
|
||||
}
|
||||
|
||||
// For idle animations, create a custom sequence: hold rotation frame for 2s, then loop breathing animation
|
||||
if (playerType === 'idle') {
|
||||
@@ -428,20 +444,38 @@ function createAtlasPlayerAnimations(spriteSheet) {
|
||||
console.log(` ✓ Created custom idle animation: ${animKey} (rotation + breath, ${idleAnimFrames.length} frames)`);
|
||||
}
|
||||
} else {
|
||||
// Standard animation
|
||||
// Standard animation (walk, cross-punch, lead-jab, etc.)
|
||||
const frameConfig = animationFramework[playerType] || { frameRate: walkFrameRate, repeat: -1 };
|
||||
if (!gameRef.anims.exists(animKey)) {
|
||||
const frameArray = frames.map(frameName => ({ key: spriteSheet, frame: frameName }));
|
||||
console.log(` - Creating ${animKey} with ${frameArray.length} frames, frameRate: ${frameConfig.frameRate}, repeat: ${frameConfig.repeat}`);
|
||||
if (frameArray.length === 0) {
|
||||
console.warn(` ⚠️ Warning: Animation has 0 frames!`);
|
||||
}
|
||||
gameRef.anims.create({
|
||||
key: animKey,
|
||||
frames: frames.map(frameName => ({ key: spriteSheet, frame: frameName })),
|
||||
frameRate: playerType === 'idle' ? idleFrameRate : walkFrameRate,
|
||||
repeat: -1
|
||||
frames: frameArray,
|
||||
frameRate: frameConfig.frameRate,
|
||||
repeat: frameConfig.repeat
|
||||
});
|
||||
console.log(` ✓ Created player animation: ${animKey} (${frames.length} frames @ ${playerType === 'idle' ? idleFrameRate : walkFrameRate} fps)`);
|
||||
console.log(` ✓ Created ${frameConfig.name} animation: ${animKey} (${frames.length} frames @ ${frameConfig.frameRate} fps, repeat: ${frameConfig.repeat})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Player atlas animations created for ${spriteSheet} (idle: ${idleFrameRate} fps, walk: ${walkFrameRate} fps)`);
|
||||
console.log(`✅ Player atlas animations created for ${spriteSheet} (idle: ${idleFrameRate} fps, walk: ${walkFrameRate} fps, punch: ${punchFrameRate} fps)`);
|
||||
|
||||
// Log all punch animations created
|
||||
const punchAnims = Object.keys(animations).filter(key => key.includes('cross-punch') || key.includes('lead-jab'));
|
||||
if (punchAnims.length > 0) {
|
||||
console.log(`🥊 Punch animations available (${punchAnims.length} total):`);
|
||||
punchAnims.forEach(animName => {
|
||||
const frameCount = animations[animName].length;
|
||||
console.log(` - ${animName}: ${frameCount} frames`);
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ No punch animations found in atlas!');
|
||||
}
|
||||
}
|
||||
|
||||
function createLegacyPlayerAnimations(spriteSheet) {
|
||||
|
||||
@@ -56,30 +56,112 @@ export class PlayerCombat {
|
||||
}
|
||||
|
||||
/**
|
||||
* Play punch animation (placeholder)
|
||||
* Map player directions to atlas compass directions
|
||||
*/
|
||||
mapDirectionToCompass(direction) {
|
||||
const directionMap = {
|
||||
'right': 'east',
|
||||
'left': 'west',
|
||||
'up': 'north',
|
||||
'down': 'south',
|
||||
'up-right': 'north-east',
|
||||
'up-left': 'north-west',
|
||||
'down-right': 'south-east',
|
||||
'down-left': 'south-west'
|
||||
};
|
||||
return directionMap[direction] || 'south';
|
||||
}
|
||||
|
||||
/**
|
||||
* Play punch animation - tries cross-punch and lead-jab with fallback to red tint
|
||||
*/
|
||||
playPunchAnimation() {
|
||||
if (!window.player) return;
|
||||
|
||||
// Apply red tint
|
||||
if (window.spriteEffects) {
|
||||
window.spriteEffects.applyAttackTint(window.player);
|
||||
const player = window.player;
|
||||
const direction = player.lastDirection || 'down';
|
||||
const compassDir = this.mapDirectionToCompass(direction);
|
||||
|
||||
// Try to play punch animation (cross-punch then lead-jab)
|
||||
const crossPunchKey = `cross-punch_${compassDir}`;
|
||||
const leadJabKey = `lead-jab_${compassDir}`;
|
||||
|
||||
console.log(`🥊 Punch attempt: direction=${direction}, compass=${compassDir}`);
|
||||
console.log(` - Trying: ${crossPunchKey} (exists: ${this.scene.anims.exists(crossPunchKey)})`);
|
||||
console.log(` - Trying: ${leadJabKey} (exists: ${this.scene.anims.exists(leadJabKey)})`);
|
||||
|
||||
// Debug: list all animations starting with cross-punch or lead-jab
|
||||
const allAnimsManager = this.scene.anims;
|
||||
const punchAnimsInScene = [];
|
||||
if (allAnimsManager.animationlist) {
|
||||
Object.keys(allAnimsManager.animationlist).forEach(key => {
|
||||
if (key.includes('cross-punch') || key.includes('lead-jab')) {
|
||||
punchAnimsInScene.push(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Play walk animation if not already playing
|
||||
if (!window.player.anims.isPlaying) {
|
||||
const direction = window.player.lastDirection || 'down';
|
||||
window.player.play(`walk_${direction}`, true);
|
||||
if (punchAnimsInScene.length > 0) {
|
||||
console.log(` - Available punch animations in scene: ${punchAnimsInScene.join(', ')}`);
|
||||
} else {
|
||||
console.warn(` - ⚠️ NO punch animations found in scene!`);
|
||||
}
|
||||
|
||||
// Remove tint after animation
|
||||
this.scene.time.delayedCall(COMBAT_CONFIG.player.punchAnimationDuration, () => {
|
||||
|
||||
let animPlayed = false;
|
||||
let playedKey = null;
|
||||
|
||||
// Try cross-punch animation first
|
||||
if (this.scene.anims.exists(crossPunchKey)) {
|
||||
console.log(` ✓ Found ${crossPunchKey}, playing...`);
|
||||
player.anims.play(crossPunchKey, true);
|
||||
animPlayed = true;
|
||||
playedKey = crossPunchKey;
|
||||
console.log(` - After play: currentAnim=${player.anims.currentAnim?.key}, visible=${player.visible}, alpha=${player.alpha}`);
|
||||
}
|
||||
// Fall back to lead-jab animation
|
||||
else if (this.scene.anims.exists(leadJabKey)) {
|
||||
console.log(` ✓ Found ${leadJabKey}, playing...`);
|
||||
player.anims.play(leadJabKey, true);
|
||||
animPlayed = true;
|
||||
playedKey = leadJabKey;
|
||||
console.log(` - After play: currentAnim=${player.anims.currentAnim?.key}, visible=${player.visible}, alpha=${player.alpha}`);
|
||||
}
|
||||
|
||||
if (animPlayed) {
|
||||
console.log(`🥊 Playing punch animation: ${playedKey}`);
|
||||
// Animation will complete naturally
|
||||
// Listen for animation complete event to return to idle
|
||||
player.once('animationcomplete', () => {
|
||||
const idleKey = `idle-${direction}`;
|
||||
if (player.anims && player.anims.exists && this.scene.anims.exists(idleKey)) {
|
||||
player.anims.play(idleKey, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback: red tint + walk animation
|
||||
console.log(`⚠️ No punch animations found (tried ${crossPunchKey}, ${leadJabKey}), using fallback (red tint)`);
|
||||
|
||||
// Apply red tint
|
||||
if (window.spriteEffects) {
|
||||
window.spriteEffects.clearAttackTint(window.player);
|
||||
window.spriteEffects.applyAttackTint(player);
|
||||
}
|
||||
// Stop animation
|
||||
window.player.anims.stop();
|
||||
});
|
||||
|
||||
// Play walk animation if not already playing
|
||||
if (!player.anims.isPlaying) {
|
||||
const walkKey = `walk-${direction}`;
|
||||
if (this.scene.anims.exists(walkKey)) {
|
||||
player.play(walkKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove tint after animation
|
||||
this.scene.time.delayedCall(COMBAT_CONFIG.player.punchAnimationDuration, () => {
|
||||
if (window.spriteEffects) {
|
||||
window.spriteEffects.clearAttackTint(player);
|
||||
}
|
||||
// Stop animation
|
||||
player.anims.stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,7 +93,7 @@ export function createPlayerBumpEffect() {
|
||||
playerCollisionBottom >= itemBottomStart &&
|
||||
playerCollisionTop <= itemBottomEnd) {
|
||||
|
||||
// Player stepped over a floor item - create one-time hop effect
|
||||
// Player stepped over a floor item - trigger punch effect
|
||||
steppedOverItems.add(itemId);
|
||||
lastHopTime = currentTime; // Update hop time
|
||||
|
||||
@@ -102,89 +102,57 @@ export function createPlayerBumpEffect() {
|
||||
steppedOverItems.delete(itemId);
|
||||
}, 2000);
|
||||
|
||||
// Create one-time hop effect
|
||||
if (playerBumpTween) {
|
||||
playerBumpTween.destroy();
|
||||
}
|
||||
|
||||
// Replace red tint effect with punch animation (cross-punch or lead-jab)
|
||||
isPlayerBumping = true;
|
||||
|
||||
// Create hop effect using visual overlay
|
||||
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);
|
||||
|
||||
// Always hop upward - negative Y values move sprite up on screen
|
||||
const hopHeight = -15; // Consistent upward hop
|
||||
|
||||
// Debug: Log the hop details
|
||||
console.log(`Hop triggered - Player Y: ${player.y}, Overlay Y: ${playerVisualOverlay.y}, Hop Height: ${hopHeight}, Target Y: ${playerVisualOverlay.y + hopHeight}`);
|
||||
console.log(`Player movement - DeltaX: ${currentX - lastPlayerPosition.x}, DeltaY: ${currentY - lastPlayerPosition.y}`);
|
||||
|
||||
// Start the hop animation with a simple up-down motion
|
||||
playerBumpTween = gameRef.tweens.add({
|
||||
targets: { hopOffset: 0 },
|
||||
hopOffset: hopHeight,
|
||||
duration: 120,
|
||||
ease: 'Power2',
|
||||
yoyo: true,
|
||||
onUpdate: (tween) => {
|
||||
if (playerVisualOverlay && playerVisualOverlay.active) {
|
||||
// Apply the hop 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 hop
|
||||
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
|
||||
}
|
||||
// Map player direction to atlas compass direction
|
||||
const directionMap = {
|
||||
'right': 'east',
|
||||
'left': 'west',
|
||||
'up': 'north',
|
||||
'down': 'south',
|
||||
'up-right': 'north-east',
|
||||
'up-left': 'north-west',
|
||||
'down-right': 'south-east',
|
||||
'down-left': 'south-west'
|
||||
};
|
||||
const compassDir = directionMap[player.direction] || 'south';
|
||||
|
||||
// Update overlay position every frame during hop
|
||||
const followInterval = setInterval(() => {
|
||||
if (!playerVisualOverlay || !playerVisualOverlay.active) {
|
||||
clearInterval(followInterval);
|
||||
return;
|
||||
}
|
||||
followPlayer();
|
||||
}, 16); // ~60fps
|
||||
// Try to play punch/jab animation if available
|
||||
const crossPunchKey = `cross-punch_${compassDir}`;
|
||||
const leadJabKey = `lead-jab_${compassDir}`;
|
||||
|
||||
// Clean up interval when hop completes
|
||||
setTimeout(() => {
|
||||
clearInterval(followInterval);
|
||||
}, 240); // Slightly longer than animation duration
|
||||
let animPlayed = false;
|
||||
|
||||
// Try cross-punch animation first
|
||||
if (gameRef.anims.exists(crossPunchKey)) {
|
||||
player.anims.play(crossPunchKey, true);
|
||||
animPlayed = true;
|
||||
console.log(`🥊 Bump: Playing cross-punch animation: ${crossPunchKey}`);
|
||||
}
|
||||
// Fall back to lead-jab animation
|
||||
else if (gameRef.anims.exists(leadJabKey)) {
|
||||
player.anims.play(leadJabKey, true);
|
||||
animPlayed = true;
|
||||
console.log(`🥊 Bump: Playing lead-jab animation: ${leadJabKey}`);
|
||||
}
|
||||
|
||||
if (animPlayed) {
|
||||
// Restore to idle after animation completes
|
||||
player.once('animationcomplete', () => {
|
||||
const animDir = typeof getAnimationKey === 'function' ? getAnimationKey(player.direction) : player.direction;
|
||||
player.anims.play(`idle-${animDir}`, true);
|
||||
isPlayerBumping = false;
|
||||
});
|
||||
} else {
|
||||
// Fallback: flash red tint for 120ms if no animation available
|
||||
console.log(`⚠️ No punch animations found (tried ${crossPunchKey}, ${leadJabKey}), using red tint fallback`);
|
||||
player.setTint(0xff4444);
|
||||
setTimeout(() => {
|
||||
player.clearTint();
|
||||
isPlayerBumping = false;
|
||||
}, 120);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user