mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
fix: Add external function bindings for NPC hub ink files
- Add bindExternalFunction method to InkEngine - Bind all required EXTERNAL functions in PersonChatConversation: - player_name() - returns player's agent name - current_mission_id() - returns active mission ID - npc_location() - returns conversation location - mission_phase() - returns mission phase (planning/active/debriefing/downtime) - operational_stress_level() - returns stress level for handler - equipment_status() - returns equipment status for Dr. Chen - Add setupExternalFunctions to PhoneChatConversation - Fixes: "Missing function binding for externals" error
This commit is contained in:
@@ -80,13 +80,7 @@ export default class PersonChatConversation {
|
||||
*/
|
||||
setupExternalFunctions() {
|
||||
if (!this.inkEngine) return;
|
||||
|
||||
// Example: Allow Ink to call game functions
|
||||
// this.inkEngine.bindFunction('unlock_door', (doorId) => {
|
||||
// console.log(`🔓 Unlocking door: ${doorId}`);
|
||||
// // Handle door unlock
|
||||
// });
|
||||
|
||||
|
||||
// Store NPC metadata in global game state
|
||||
if (!window.gameState) {
|
||||
window.gameState = {};
|
||||
@@ -94,14 +88,54 @@ export default class PersonChatConversation {
|
||||
if (!window.gameState.npcInteractions) {
|
||||
window.gameState.npcInteractions = {};
|
||||
}
|
||||
|
||||
|
||||
// Bind EXTERNAL functions that return values
|
||||
// These are called from ink scripts with parentheses: {player_name()}
|
||||
|
||||
// Player name - return player's agent name or default
|
||||
this.inkEngine.bindExternalFunction('player_name', () => {
|
||||
return window.gameState?.playerName || 'Agent';
|
||||
});
|
||||
|
||||
// Current mission ID - return active mission identifier
|
||||
this.inkEngine.bindExternalFunction('current_mission_id', () => {
|
||||
return window.gameState?.currentMissionId || 'mission_001';
|
||||
});
|
||||
|
||||
// NPC location - where the conversation is happening
|
||||
this.inkEngine.bindExternalFunction('npc_location', () => {
|
||||
// Return location based on NPC or default
|
||||
if (this.npc.id === 'dr_chen') {
|
||||
return window.gameState?.npcLocation || 'lab';
|
||||
} else if (this.npc.id === 'director_netherton') {
|
||||
return window.gameState?.npcLocation || 'office';
|
||||
} else if (this.npc.id === 'haxolottle') {
|
||||
return window.gameState?.npcLocation || 'handler_station';
|
||||
}
|
||||
return window.gameState?.npcLocation || 'safehouse';
|
||||
});
|
||||
|
||||
// Mission phase - what part of the mission we're in
|
||||
this.inkEngine.bindExternalFunction('mission_phase', () => {
|
||||
return window.gameState?.missionPhase || 'downtime';
|
||||
});
|
||||
|
||||
// Operational stress level - for handler conversations
|
||||
this.inkEngine.bindExternalFunction('operational_stress_level', () => {
|
||||
return window.gameState?.operationalStressLevel || 'low';
|
||||
});
|
||||
|
||||
// Equipment status - for Dr. Chen conversations
|
||||
this.inkEngine.bindExternalFunction('equipment_status', () => {
|
||||
return window.gameState?.equipmentStatus || 'nominal';
|
||||
});
|
||||
|
||||
// Set variables in the Ink engine using setVariable instead of bindVariable
|
||||
this.inkEngine.setVariable('last_interaction_type', 'person');
|
||||
this.inkEngine.setVariable('player_name', 'Player');
|
||||
|
||||
|
||||
// Sync NPC items to Ink variables
|
||||
this.syncItemsToInk();
|
||||
|
||||
|
||||
// Set up event listener for item changes
|
||||
if (window.eventDispatcher) {
|
||||
this._itemsChangedListener = (data) => {
|
||||
|
||||
@@ -71,10 +71,13 @@ export default class PhoneChatConversation {
|
||||
|
||||
// Note: We don't set npc_name variable here because it causes issues with state serialization.
|
||||
// The NPC display name is handled in the UI layer instead.
|
||||
|
||||
|
||||
this.storyLoaded = true;
|
||||
this.storyEnded = false;
|
||||
|
||||
|
||||
// Set up external functions
|
||||
this.setupExternalFunctions();
|
||||
|
||||
// Sync NPC items to Ink variables
|
||||
this.syncItemsToInk();
|
||||
|
||||
@@ -98,6 +101,58 @@ export default class PhoneChatConversation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up external functions for Ink story
|
||||
* These allow Ink to call game functions and get dynamic values
|
||||
*/
|
||||
setupExternalFunctions() {
|
||||
if (!this.engine || !this.engine.story) return;
|
||||
|
||||
// Bind EXTERNAL functions that return values
|
||||
// These are called from ink scripts with parentheses: {player_name()}
|
||||
|
||||
// Player name - return player's agent name or default
|
||||
this.engine.bindExternalFunction('player_name', () => {
|
||||
return window.gameState?.playerName || 'Agent';
|
||||
});
|
||||
|
||||
// Current mission ID - return active mission identifier
|
||||
this.engine.bindExternalFunction('current_mission_id', () => {
|
||||
return window.gameState?.currentMissionId || 'mission_001';
|
||||
});
|
||||
|
||||
// NPC location - where the conversation is happening
|
||||
this.engine.bindExternalFunction('npc_location', () => {
|
||||
const npc = this.npcManager.getNPC(this.npcId);
|
||||
// Return location based on NPC or default
|
||||
if (this.npcId === 'dr_chen' || npc?.id === 'dr_chen') {
|
||||
return window.gameState?.npcLocation || 'lab';
|
||||
} else if (this.npcId === 'director_netherton' || npc?.id === 'director_netherton') {
|
||||
return window.gameState?.npcLocation || 'office';
|
||||
} else if (this.npcId === 'haxolottle' || npc?.id === 'haxolottle') {
|
||||
return window.gameState?.npcLocation || 'handler_station';
|
||||
}
|
||||
return window.gameState?.npcLocation || 'safehouse';
|
||||
});
|
||||
|
||||
// Mission phase - what part of the mission we're in
|
||||
this.engine.bindExternalFunction('mission_phase', () => {
|
||||
return window.gameState?.missionPhase || 'downtime';
|
||||
});
|
||||
|
||||
// Operational stress level - for handler conversations
|
||||
this.engine.bindExternalFunction('operational_stress_level', () => {
|
||||
return window.gameState?.operationalStressLevel || 'low';
|
||||
});
|
||||
|
||||
// Equipment status - for Dr. Chen conversations
|
||||
this.engine.bindExternalFunction('equipment_status', () => {
|
||||
return window.gameState?.equipmentStatus || 'nominal';
|
||||
});
|
||||
|
||||
console.log(`✅ External functions bound for ${this.npcId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a specific knot in the story
|
||||
* @param {string} knotName - Name of the knot to navigate to
|
||||
|
||||
@@ -116,7 +116,7 @@ export default class InkEngine {
|
||||
|
||||
setVariable(name, value) {
|
||||
if (!this.story) throw new Error('Story not loaded');
|
||||
|
||||
|
||||
// Let Ink handle the value type conversion through the indexer
|
||||
// which properly wraps values in Runtime.Value objects
|
||||
try {
|
||||
@@ -125,4 +125,16 @@ export default class InkEngine {
|
||||
console.warn(`⚠️ Failed to set variable ${name}:`, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind an external function that Ink can call
|
||||
bindExternalFunction(name, func) {
|
||||
if (!this.story) throw new Error('Story not loaded');
|
||||
|
||||
try {
|
||||
this.story.BindExternalFunction(name, func);
|
||||
console.log(`✅ Bound external function: ${name}`);
|
||||
} catch (err) {
|
||||
console.warn(`⚠️ Failed to bind external function ${name}:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user