feat: Add sprite padding constants and adjust depth calculations for player and NPC sprites

This commit is contained in:
Z. Cliffe Schreuders
2026-02-15 00:30:24 +00:00
parent ba13b6aa73
commit a6458a9ff2
6 changed files with 90 additions and 12 deletions

View File

@@ -22,7 +22,7 @@ import { PlayerCombat } from '../systems/player-combat.js';
import { NPCCombat } from '../systems/npc-combat.js';
import { ApiClient } from '../api-client.js'; // Import to ensure window.ApiClient is set
import { getTutorialManager } from '../systems/tutorial-manager.js';
import { TILE_SIZE } from '../utils/constants.js';
import { TILE_SIZE, SPRITE_PADDING_BOTTOM_ATLAS, SPRITE_PADDING_BOTTOM_LEGACY } from '../utils/constants.js';
// Global variables that will be set by main.js
let gameScenario;
@@ -885,7 +885,10 @@ export async function create() {
} else {
// NPC was out of range - treat click as a movement request
// Calculate floor-level destination at the NPC's position, offset to stop short
const npcBottomY = npcAtPosition.y + (npcAtPosition.height * (1 - (npcAtPosition.originY || 0.5)));
// Account for sprite padding (16px for atlas sprites)
const spriteCenterToBottom = npcAtPosition.height * (1 - (npcAtPosition.originY || 0.5));
const paddingOffset = npcAtPosition.isAtlas ? SPRITE_PADDING_BOTTOM_ATLAS : SPRITE_PADDING_BOTTOM_LEGACY;
const npcBottomY = npcAtPosition.y + spriteCenterToBottom - paddingOffset;
const stopShortOffset = TILE_SIZE * 0.75; // Stop 24 pixels short (3/4 tile)
movePlayerToPoint(npcAtPosition.x, npcBottomY + stopShortOffset);
return;

View File

@@ -10,7 +10,9 @@ import {
PLAYER_FEET_OFFSET_Y,
ROOM_CHECK_THRESHOLD,
CLICK_INDICATOR_SIZE,
CLICK_INDICATOR_DURATION
CLICK_INDICATOR_DURATION,
SPRITE_PADDING_BOTTOM_ATLAS,
SPRITE_PADDING_BOTTOM_LEGACY
} from '../utils/constants.js?v=8';
export let player = null;
@@ -94,6 +96,9 @@ export function createPlayer(gameInstance) {
player = gameInstance.add.sprite(startRoomPosition.x, startRoomPosition.y, playerSprite, initialFrame);
gameInstance.physics.add.existing(player);
// Store atlas detection flag for depth calculations
player.isAtlas = isAtlas;
// Keep the character at original size
player.setScale(1);
@@ -624,8 +629,12 @@ export function movePlayerToPoint(x, y) {
}
function updatePlayerDepth(x, y) {
// Get the bottom of the player sprite (feet position)
const playerBottomY = y + (player.height * player.scaleY) / 2;
// Get the bottom of the player sprite, accounting for padding
// Atlas sprites (80x80) have 16px padding at bottom, legacy sprites (64x64) have minimal padding
// Use actual y parameter so depth follows visual position (including during death animations)
const spriteCenterToBottom = (player.height * player.scaleY) / 2;
const paddingOffset = player.isAtlas ? SPRITE_PADDING_BOTTOM_ATLAS : SPRITE_PADDING_BOTTOM_LEGACY;
const playerBottomY = y + spriteCenterToBottom - paddingOffset;
// Simple depth calculation: world Y position + layer offset
const playerDepth = playerBottomY + 0.5; // World Y + sprite layer offset
@@ -637,8 +646,8 @@ function updatePlayerDepth(x, y) {
// Debug logging - only show when depth actually changes significantly
const lastDepth = player.lastDepth || 0;
if (Math.abs(playerDepth - lastDepth) > 25) { // Reduced threshold for finer granularity
console.log(`Player depth: ${playerDepth} (World Y: ${playerBottomY})`);
console.log(` Player layers: worldY(${playerBottomY}) + 0.5`);
console.log(`Player depth: ${playerDepth} (Feet Y: ${playerBottomY}, Padding: ${paddingOffset}px)`);
console.log(` Player layers: feetY(${playerBottomY}) + 0.5`);
player.lastDepth = playerDepth;
}
}

View File

@@ -153,6 +153,35 @@ function playNPCDeathAnimation(npcId, sprite) {
if (sprite.anims.isPlaying) {
sprite.anims.stop();
}
// Store original origin for restoration
const originalOriginY = sprite.originY;
// Add animation update listener to progressively shift visual display downward
sprite.on('animationupdate', (anim, frame) => {
if (anim.key === deathAnimKey) {
// Calculate progress through animation (0 to 1)
const totalFrames = anim.getTotalFrames();
const currentFrame = frame.index;
const progress = currentFrame / totalFrames;
// Shift sprite's visual display downward by adjusting origin
// This moves the texture down without changing sprite.y (keeps depth constant)
// Decrease originY by ~0.5 to shift texture down by half sprite height (~40px for 80px sprite)
const originOffset = progress * 0.5;
sprite.setOrigin(sprite.originX, originalOriginY - originOffset);
}
});
// Clean up listener when animation completes
sprite.once('animationcomplete', (anim) => {
if (anim.key === deathAnimKey) {
sprite.off('animationupdate');
// Origin stays shifted - defeated body appears lower visually but sprite.y unchanged
// Depth remains constant, naturally rendering behind standing characters at same Y
}
});
sprite.play(deathAnimKey);
console.log(`💀 Playing NPC death animation: ${deathAnimKey}`);
} else {

View File

@@ -7,7 +7,7 @@
* @module npc-sprites
*/
import { TILE_SIZE } from '../utils/constants.js?v=8';
import { TILE_SIZE, SPRITE_PADDING_BOTTOM_ATLAS, SPRITE_PADDING_BOTTOM_LEGACY } from '../utils/constants.js?v=8';
/**
* Create an NPC sprite in the game world
@@ -78,6 +78,7 @@ export function createNPCSprite(scene, npc, roomData) {
const sprite = scene.add.sprite(worldPos.x, worldPos.y, spriteSheet, initialFrame);
sprite.npcId = npc.id; // Tag for identification
sprite._isNPC = true; // Mark as NPC sprite
sprite.isAtlas = isAtlas; // Store for depth calculation
console.log(`🎭 NPC ${npc.id} created with ${isAtlas ? 'atlas' : 'legacy'} sprite (${spriteSheet}), initial frame: ${initialFrame}`);
@@ -556,16 +557,20 @@ export function setupNPCAnimations(scene, sprite, spriteSheet, config, npcId) {
/**
* Update NPC sprite depth based on Y position
*
* Uses same system as player (bottomY + 0.5) to ensure correct
* perspective in top-down view.
* Uses same system as player (feetY + 0.5) to ensure correct
* perspective in top-down view. Accounts for sprite padding.
*
* @param {Phaser.Sprite} sprite - NPC sprite to update
*/
export function updateNPCDepth(sprite) {
if (!sprite || !sprite.body) return;
// Get the bottom of the sprite (feet position)
const spriteBottomY = sprite.y + (sprite.displayHeight / 2);
// Get the bottom of the sprite, accounting for padding
// Atlas sprites (80x80) have 16px padding at bottom, legacy sprites (64x64) have minimal padding
// Use actual sprite.y so depth follows visual position (including during death animations)
const spriteCenterToBottom = sprite.displayHeight / 2;
const paddingOffset = sprite.isAtlas ? SPRITE_PADDING_BOTTOM_ATLAS : SPRITE_PADDING_BOTTOM_LEGACY;
const spriteBottomY = sprite.y + spriteCenterToBottom - paddingOffset;
// Set depth using standard formula
const depth = spriteBottomY + 0.5; // World Y + sprite layer offset

View File

@@ -99,6 +99,32 @@ function playPlayerDeathAnimation() {
const deathAnimKey = `falling-back-death_${compassDir}`;
if (player.scene.anims.exists(deathAnimKey)) {
// Store original origin for visual shift
const originalOriginY = player.originY;
// Add animation update listener to progressively shift visual display downward
player.on('animationupdate', (anim, frame) => {
if (anim.key === deathAnimKey) {
// Calculate progress through animation (0 to 1)
const totalFrames = anim.getTotalFrames();
const currentFrame = frame.index;
const progress = currentFrame / totalFrames;
// Shift player's visual display downward by adjusting origin
// Decrease originY by ~0.4 to shift texture down (~32px for 80px sprite)
const originOffset = progress * 0.4;
player.setOrigin(player.originX, originalOriginY - originOffset);
}
});
// Clean up listener when animation completes
player.once('animationcomplete', (anim) => {
if (anim.key === deathAnimKey) {
player.off('animationupdate');
// Origin stays shifted - defeated player appears lower but depth unchanged
}
});
player.play(deathAnimKey);
console.log(`💀 Playing player death animation: ${deathAnimKey}`);
} else {

View File

@@ -26,6 +26,12 @@ export const CLICK_INDICATOR_DURATION = 800; // milliseconds
export const CLICK_INDICATOR_SIZE = 20; // pixels
export const PLAYER_FEET_OFFSET_Y = 30; // Adjust based on your sprite's feet position (64px sprite)
// Sprite dimensions and padding
export const SPRITE_SIZE_ATLAS = 80; // Atlas sprites (PixelLab) are 80x80px
export const SPRITE_SIZE_LEGACY = 64; // Legacy sprites are 64x64px
export const SPRITE_PADDING_BOTTOM_ATLAS = 6; // Atlas sprites have 16px padding at bottom
export const SPRITE_PADDING_BOTTOM_LEGACY = 4; // Legacy sprites have minimal bottom padding
// Room visibility settings
export const HIDE_ROOMS_INITIALLY = true;
export const HIDE_ROOMS_ON_EXIT = false;