mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
feat(npc-interaction): Enhance NPC interaction system with click handling and distance checks
This commit is contained in:
@@ -597,7 +597,39 @@ export function create() {
|
||||
const worldX = this.cameras.main.scrollX + pointer.x;
|
||||
const worldY = this.cameras.main.scrollY + pointer.y;
|
||||
|
||||
// Check for objects at the clicked position first
|
||||
// Check for NPC sprites at the clicked position first
|
||||
const npcAtPosition = findNPCAtPosition(worldX, worldY);
|
||||
if (npcAtPosition) {
|
||||
// Try to interact with the NPC
|
||||
if (window.tryInteractWithNPC) {
|
||||
const player = window.player;
|
||||
if (player) {
|
||||
window.preventPlayerMovement = true;
|
||||
const previousX = player.x;
|
||||
const previousY = player.y;
|
||||
|
||||
// Try the interaction
|
||||
window.tryInteractWithNPC(npcAtPosition);
|
||||
|
||||
// If the interaction didn't move the player (NPC was out of range),
|
||||
// treat this as a movement request to that NPC instead
|
||||
if (player.x === previousX && player.y === previousY) {
|
||||
// Reset the flag and move toward the NPC
|
||||
window.preventPlayerMovement = false;
|
||||
movePlayerToPoint(npcAtPosition.x, npcAtPosition.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// Interaction was successful
|
||||
setTimeout(() => {
|
||||
window.preventPlayerMovement = false;
|
||||
}, 100);
|
||||
return; // Exit early after handling the interaction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for objects at the clicked position
|
||||
const objectsAtPosition = findObjectsAtPosition(worldX, worldY);
|
||||
|
||||
if (objectsAtPosition.length > 0) {
|
||||
@@ -748,6 +780,46 @@ function findObjectsAtPosition(worldX, worldY) {
|
||||
return objectsAtPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an NPC sprite at the clicked position
|
||||
* @param {number} worldX - World X coordinate
|
||||
* @param {number} worldY - World Y coordinate
|
||||
* @returns {Object|null} NPC sprite if found, null otherwise
|
||||
*/
|
||||
function findNPCAtPosition(worldX, worldY) {
|
||||
let closestNPC = null;
|
||||
let closestDistance = Infinity;
|
||||
|
||||
// Check all rooms for NPC sprites at the given position
|
||||
Object.entries(window.rooms).forEach(([roomId, room]) => {
|
||||
if (room.npcSprites && Array.isArray(room.npcSprites)) {
|
||||
room.npcSprites.forEach(npcSprite => {
|
||||
if (npcSprite && !npcSprite.destroyed && npcSprite.visible) {
|
||||
// Get NPC bounds
|
||||
const bounds = npcSprite.getBounds();
|
||||
|
||||
// Check if click is within bounds
|
||||
if (worldX >= bounds.left && worldX <= bounds.right &&
|
||||
worldY >= bounds.top && worldY <= bounds.bottom) {
|
||||
// Calculate distance from click to NPC center
|
||||
const dx = worldX - npcSprite.x;
|
||||
const dy = worldY - npcSprite.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Keep the closest NPC
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestNPC = npcSprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return closestNPC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize keyPins from 0-100 scale to 25-65 scale in entire scenario
|
||||
*
|
||||
|
||||
@@ -456,8 +456,16 @@ export class PersonChatMinigame extends MinigameScene {
|
||||
console.log('⏸️ Blocks finished, checking for more dialogue...');
|
||||
setTimeout(() => {
|
||||
const nextLine = this.conversation.continue();
|
||||
|
||||
// Store for choice handling
|
||||
this.lastResult = nextLine;
|
||||
|
||||
if (nextLine.text && nextLine.text.trim()) {
|
||||
this.displayAccumulatedDialogue(nextLine);
|
||||
} else if (nextLine.choices && nextLine.choices.length > 0) {
|
||||
// Back to choices - display them
|
||||
console.log(`📋 Back to choices: ${nextLine.choices.length} options available`);
|
||||
this.ui.showChoices(nextLine.choices);
|
||||
} else if (nextLine.hasEnded) {
|
||||
this.endConversation();
|
||||
}
|
||||
|
||||
@@ -961,8 +961,31 @@ export function tryInteractWithNearest() {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle NPC interaction by sprite reference
|
||||
export function tryInteractWithNPC(npcSprite) {
|
||||
if (!npcSprite || !npcSprite._isNPC) {
|
||||
return;
|
||||
}
|
||||
|
||||
const player = window.player;
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if NPC is within interaction range of the player
|
||||
const distanceSq = getInteractionDistance(player, npcSprite.x, npcSprite.y);
|
||||
const distance = Math.sqrt(distanceSq);
|
||||
|
||||
// Only interact if within range
|
||||
if (distance <= INTERACTION_RANGE) {
|
||||
handleObjectInteraction(npcSprite);
|
||||
}
|
||||
// If out of range, the click handler will treat it as a movement request instead
|
||||
}
|
||||
|
||||
// Export for global access
|
||||
window.checkObjectInteractions = checkObjectInteractions;
|
||||
window.handleObjectInteraction = handleObjectInteraction;
|
||||
window.handleContainerInteraction = handleContainerInteraction;
|
||||
window.tryInteractWithNearest = tryInteractWithNearest;
|
||||
window.tryInteractWithNPC = tryInteractWithNPC;
|
||||
|
||||
@@ -12,10 +12,11 @@ export class NPCTalkIconSystem {
|
||||
this.scene = scene;
|
||||
this.npcIcons = new Map(); // { npcId: { npc, icon, sprite } }
|
||||
// Offset from NPC position - use whole pixels to avoid sub-pixel rendering
|
||||
this.ICON_OFFSET = { x: 0, y: -48 };
|
||||
this.ICON_OFFSET = { x: 0, y: 0 };
|
||||
this.INTERACTION_RANGE = 64; // Pixels
|
||||
this.UPDATE_INTERVAL = 200; // ms between updates
|
||||
this.lastUpdate = 0;
|
||||
this.ICON_WIDTH = 21; // Talk icon width in pixels
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +49,14 @@ export class NPCTalkIconSystem {
|
||||
if (this.npcIcons.has(spriteObj.npcId)) return;
|
||||
|
||||
try {
|
||||
// Calculate pixel-perfect position (round to avoid sub-pixel rendering)
|
||||
const iconX = Math.round(spriteObj.x + this.ICON_OFFSET.x);
|
||||
// Calculate the offset to align icon's right edge with sprite's right edge
|
||||
// Get sprite's actual width in pixels (accounting for scale)
|
||||
const spriteWidth = spriteObj.width;
|
||||
// Offset from sprite center to align right edges
|
||||
const offsetX = 0; //(spriteWidth / 2) - (scaledIconWidth / 2);
|
||||
|
||||
// Calculate pixel-perfect position (round to whole pixels)
|
||||
const iconX = Math.round(spriteObj.x + offsetX);
|
||||
const iconY = Math.round(spriteObj.y + this.ICON_OFFSET.y);
|
||||
|
||||
// Create the icon image
|
||||
@@ -58,18 +65,17 @@ export class NPCTalkIconSystem {
|
||||
// Hide by default
|
||||
icon.setVisible(false);
|
||||
icon.setDepth(spriteObj.depth + 1);
|
||||
icon.setScale(0.75); // Slightly smaller than full size
|
||||
// Disable antialiasing to keep pixels sharp
|
||||
icon.setOrigin(0.5, 0.5);
|
||||
// icon.setOrigin(0.5, 0.5);
|
||||
|
||||
// Store reference
|
||||
// Store reference with calculated offset for consistent positioning
|
||||
this.npcIcons.set(spriteObj.npcId, {
|
||||
npc: spriteObj,
|
||||
icon: icon,
|
||||
visible: false
|
||||
visible: false,
|
||||
offsetX: offsetX // Store for consistent updates
|
||||
});
|
||||
|
||||
console.log(`💬 Created talk icon for NPC: ${spriteObj.npcId}`);
|
||||
console.log(`💬 Created talk icon for NPC: ${spriteObj.npcId} at offset x=${offsetX}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Error creating talk icon for ${spriteObj.npcId}:`, error);
|
||||
}
|
||||
@@ -107,8 +113,8 @@ export class NPCTalkIconSystem {
|
||||
}
|
||||
|
||||
// Update position to follow NPC with pixel-perfect alignment
|
||||
// Round to whole pixels to avoid sub-pixel rendering
|
||||
const newX = Math.round(iconData.npc.x + this.ICON_OFFSET.x);
|
||||
// Use stored offset to ensure consistent positioning without recalculating bounds
|
||||
const newX = Math.round(iconData.npc.x + iconData.offsetX);
|
||||
const newY = Math.round(iconData.npc.y + this.ICON_OFFSET.y);
|
||||
iconData.icon.setPosition(newX, newY);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user