fix: refactor item giving and unlock handling for improved async processing and server synchronization

This commit is contained in:
Z. Cliffe Schreuders
2026-02-18 23:39:21 +00:00
parent c40091e071
commit bbbe6169b6
4 changed files with 46 additions and 21 deletions

View File

@@ -99,17 +99,23 @@ export function processGameActionTags(tags, ui) {
break;
}
const giveResult = window.NPCGameBridge.giveItem(npcId, itemType);
if (giveResult.success) {
result.success = true;
result.message = `📦 Received: ${giveResult.item.name}`;
if (ui) ui.showNotification(result.message, 'success');
console.log('✅ Item given successfully:', giveResult);
} else {
result.message = `⚠️ ${giveResult.error}`;
if (ui) ui.showNotification(result.message, 'warning');
console.warn('⚠️ Item give failed:', giveResult);
}
// giveItem is async (awaits addToInventory so server inventory is confirmed).
// Use .then() since we can't await inside forEach.
// Mark result as success optimistically - the conversation continues
// immediately while the inventory sync happens in the background.
result.success = true;
result.message = `📦 Receiving: ${itemType}`;
window.NPCGameBridge.giveItem(npcId, itemType).then(giveResult => {
if (giveResult.success) {
console.log('✅ Item given and server inventory synced:', giveResult);
if (ui) ui.showNotification(`📦 Received: ${giveResult.item.name}`, 'success');
} else {
console.warn('⚠️ Item give failed:', giveResult);
if (ui) ui.showNotification(`⚠️ ${giveResult.error}`, 'warning');
}
}).catch(error => {
console.error('❌ giveItem error:', error);
});
} else {
result.message = '⚠️ give_item requires item type parameter';
console.warn(result.message);

View File

@@ -392,12 +392,20 @@ export function startKeySelectionMinigame(lockable, type, playerKeys, requiredKe
requiredKeyId: requiredKeyId,
onComplete: (success, result) => {
if (success) {
console.log('KEY SELECTION SUCCESS');
window.gameAlert('Successfully unlocked with the correct key!', 'success', 'Unlock Successful', 4000);
// Detect which mode completed the unlock while currentMinigame is still live.
// keyMode is true → player used a physical key
// keyMode is false → player switched to and completed lockpick mode
const keyMode = window.MinigameFramework?.currentMinigame?.keyMode;
const unlockMethod = keyMode === false ? 'lockpick' : 'key';
console.log(`🔓 KEY SELECTION SUCCESS via method='${unlockMethod}' (keyMode=${keyMode})`);
const successMsg = unlockMethod === 'lockpick'
? 'Successfully picked the lock!'
: 'Successfully unlocked with the correct key!';
window.gameAlert(successMsg, 'success', 'Unlock Successful', 4000);
// Small delay to ensure minigame cleanup completes before room loading
if (unlockTargetCallback) {
setTimeout(() => {
unlockTargetCallback(lockable, type, lockable.layer);
unlockTargetCallback(lockable, type, lockable.layer, unlockMethod);
}, 100);
}
} else {

View File

@@ -171,7 +171,7 @@ export class NPCGameBridge {
* @param {string} itemType - Type of item to give (optional - gives first if null)
* @returns {Object} Result with success status
*/
giveItem(npcId, itemType = null) {
async giveItem(npcId, itemType = null) {
if (!npcId) {
const result = { success: false, error: 'No npcId provided' };
this._logAction('giveItem', { npcId, itemType }, result);
@@ -224,10 +224,18 @@ export class NPCGameBridge {
texture: { key: item.type }
};
// Add to player inventory
window.addToInventory(tempSprite);
// Await addToInventory so server inventory is confirmed before removing from NPC.
// This prevents the race condition where validate_unlock is called before the
// inventory POST /inventory request completes, causing a spurious 422.
const added = await window.addToInventory(tempSprite);
// Remove from NPC's inventory
if (!added) {
// addToInventory returns false for duplicates (already in inventory) or server rejection.
// Treat already-in-inventory as success; true failures are logged by addToInventory itself.
console.warn(`[NPCGameBridge] addToInventory returned false for ${item.type} from ${npcId} - may already be in inventory`);
}
// Remove from NPC's inventory (after server confirms)
npc.itemsHeld.splice(itemIndex, 1);
// Emit event to update Ink variables

View File

@@ -161,9 +161,12 @@ export function handleUnlock(lockable, type) {
if (playerKeys.length > 0) {
// Keys take priority - go straight to key selection
console.log('KEYS AVAILABLE - STARTING KEY SELECTION');
// Wrap unlockTarget to notify server first
const unlockWithServerNotification = async (lockable, type, layer) => {
const serverResponse = await notifyServerUnlock(lockable, type, 'key');
// Wrap unlockTarget to notify server first.
// method is passed from the key selection minigame's onComplete:
// 'key' → player used a physical key from inventory
// 'lockpick' → player switched to and completed pick mode
const unlockWithServerNotification = async (lockable, type, layer, method = 'key') => {
const serverResponse = await notifyServerUnlock(lockable, type, method);
unlockTarget(lockable, type, layer, serverResponse);
};
startKeySelectionMinigame(lockable, type, playerKeys, requiredKey, unlockWithServerNotification);