/** * $Id: zdlg_include_i.nss,v 1.2 2005/08/07 04:38:30 pspeed Exp $ * * Include file for using the Z-Dialog runtime conversation * system. * * This system allows a user to define conversation trees at * runtime and handle the selections programmatically. Really * useful for menu systems without script explosion. * * Copyright (c) 2004 Paul Speed - BSD licensed. * NWN Tools - http://nwntools.sf.net/ */ #include "pg_lists_i" // Constants defined for dialog events const int DLG_INIT = 0; // new dialog is started const int DLG_PAGE_INIT = 1; // a new page is started const int DLG_SELECTION = 2; // item is selected const int DLG_ABORT = 3; // dialog was aborted const int DLG_END = 4; // dialog ended normally const string DLG_CURRENT_HANDLER = "currentDialog"; const string DLG_HANDLER = "dialog"; const string DLG_PROMPT = "zdlgPrompt"; const string DLG_RESPONSE_LIST = "zdlgResponseList"; const string DLG_RESPONSE_LIST_HOLDER = "zdlgResponseHolder"; const string DLG_EVENT_TYPE = "zdlgEventType"; const string DLG_EVENT_SELECTION = "zdlgEventSelection"; const string DLG_PAGE_ID = "zdlgPageId"; const string DLG_ITEM = "zdlgItem"; const string DLG_ITEM_CONVERSER = "zdlgConverser"; const string DLG_STATE = "zdlgState"; const string DLG_START_ENTRY = "zdlgStartEntry"; const string DLG_HAS_PREV = "zdlgHasPrevious"; const string DLG_HAS_NEXT = "zdlgHasNext"; const string DLG_HAS_END = "zdlgHasEnd"; // Some state constants that the zdlg_page_init check // can use to determine current conversation state. const int DLG_STATE_INIT = 0; const int DLG_STATE_RUNNING = 1; const int DLG_STATE_ENDED = -1; // The base token for the dialog inserts. +0 is the // prompt. +1 - +13 is the item text. These values // must match the .dlg file exactly. const int DLG_BASE_TOKEN = 4200; // Returns the current PC speaker for this dialog. // This has some enhanced features to work around bioware // limitations with item dialogs. object GetPcDlgSpeaker(); // Sets the new current dialog handler script for the current conversation. // This allows on the fly conversation changes and linking. This must // be called within a conversation related event. void SetCurrentDlgHandlerScript( string script ); // Returns the current dialog handler script for the current conversation. string GetCurrentDlgHandlerScript(); // Returns the current conversation's default dialog handler script if // it has one defined. This is used when there is otherwise // not a current handler script set. string GetDefaultDlgHandlerScript( object oNPC = OBJECT_SELF ); // Sets the prompt that will be displayed in the dialog // when talking to the current speaker. void SetDlgPrompt( string prompt ); // Returns the current prompt that will be displayed in the // dialog when talking to the current speaker. string GetDlgPrompt(); // Set to TRUE if the end dialog selection should be shown // on every page. FALSE if not. void SetShowEndSelection( int flag ); // Returns TRUE if the end dialog selection should be shown // on every page. FALSE if not. int GetShowEndSelection(); // Sets the list of responses that will be displayed in the // dialog when talking to the current speaker. void SetDlgResponseList( string listId, object oListHolder ); // Returns the list id for the list of responses that will be // displayed in the dialog when talking to the current speaker. string GetDlgResponseList(); // Returns the dialog type of event that caused the handler // script to be executed. int GetDlgEventType(); // Returns the selected item in a DLG_SELECTION event. int GetDlgSelection(); // Sets a page string that the handler scripts can use to track // progress through the conversation. This is really just a // convenience function that tacks a local var onto the PC dlg // speaker. It has the added benefit of getting auto-cleaned // with the dialog clean-up. void SetDlgPageString( string page ); // Returns a page string that the handler scripts can use to track // progress through the conversation. string GetDlgPageString(); // Sets a page integer that the handler scripts can use to track // progress through the conversation. This is really just a // convenience function that tacks a local var onto the PC dlg // speaker. It has the added benefit of getting auto-cleaned // with the dialog clean-up. void SetDlgPageInt( int page ); // Returns a page integer that the handler scripts can use to track // progress through the conversation. int GetDlgPageInt(); // Called to initiate a conversation programmatically between // the dialog source and the object to converse with. If // dlgHandler is "" then the object's default script will be used. void StartDlg( object oPC, object oObjectToConverseWith, string dlgHandler = "", int bPrivate = FALSE, int bPlayHello = TRUE, int bZoom = TRUE ); // Ends the current conversation and will fire the DLG_END event. void EndDlg(); // Returns the current PC speaker for this dialog. // This has some enhanced features to work around bioware // limitations with item dialogs. object GetPcDlgSpeaker() { object oPC = GetPCSpeaker(); if( oPC == OBJECT_INVALID ) { // See if we're an item and if we're connected to a PC already. // Note: GetItemActivator won't work in multiplayer because other // players will be trouncing on its state. oPC = GetLocalObject( OBJECT_SELF, DLG_ITEM_CONVERSER ); } if( oPC == OBJECT_INVALID ) PrintString( "WARNING: Unable to retrieve a PC speaker." ); return( oPC ); } // Sets the new current dialog handler script for the current conversation. // This allows on the fly conversation changes and linking. This must // be called within a conversation related event. void SetCurrentDlgHandlerScript( string script ) { SetLocalString( GetPcDlgSpeaker(), DLG_CURRENT_HANDLER, script ); } // Returns the current dialog handler script for the current conversation. string GetCurrentDlgHandlerScript() { return( GetLocalString( GetPcDlgSpeaker(), DLG_CURRENT_HANDLER ) ); } // Returns the current conversation's default dialog handler script if // it has one defined. This is used when there is otherwise // not a current handler script set. string GetDefaultDlgHandlerScript( object oNPC = OBJECT_SELF ) { return( GetLocalString( oNPC, DLG_HANDLER ) ); } // Sets the prompt that will be displayed in the dialog // when talking to the current speaker. void SetDlgPrompt( string prompt ) { SetLocalString( GetPcDlgSpeaker(), DLG_PROMPT, prompt ); } // Returns the current prompt that will be displayed in the // dialog when talking to the current speaker. string GetDlgPrompt() { return( GetLocalString( GetPcDlgSpeaker(), DLG_PROMPT ) ); } // Set to TRUE if the end dialog selection should be shown // on every page. FALSE if not. void SetShowEndSelection( int flag) { SetLocalInt( GetPcDlgSpeaker(), DLG_HAS_END, flag ); } // Returns TRUE if the end dialog selection should be shown // on every page. FALSE if not. int GetShowEndSelection() { return( GetLocalInt( GetPcDlgSpeaker(), DLG_HAS_END ) ); } // Sets the list of responses that will be displayed in the // dialog when talking to the current speaker. void SetDlgResponseList( string listId, object oListHolder ) { object oSpeaker = GetPcDlgSpeaker(); SetLocalObject( oSpeaker, DLG_RESPONSE_LIST_HOLDER, oListHolder ); SetLocalString( oSpeaker, DLG_RESPONSE_LIST, listId ); //DeleteLocalInt( oSpeaker, DLG_HAS_PREV ); //DeleteLocalInt( oSpeaker, DLG_HAS_NEXT ); //DeleteLocalInt( oSpeaker, DLG_START_ENTRY ); } // Returns the list id for the list of responses that will be // displayed in the dialog when talking to the current speaker. string GetDlgResponseList() { return( GetLocalString( GetPcDlgSpeaker(), DLG_RESPONSE_LIST ) ); } // Returns the dialog type of event that caused the handler // script to be executed. int GetDlgEventType() { return( GetLocalInt( GetPcDlgSpeaker(), DLG_EVENT_TYPE ) ); } // Returns the selected item in a DLG_SELECTION event. int GetDlgSelection() { return( GetLocalInt( GetPcDlgSpeaker(), DLG_EVENT_SELECTION ) ); } // Sets a page string that the scripts can use to track // progress through the conversation. void SetDlgPageString( string page ) { object oSpeaker = GetPcDlgSpeaker(); SetLocalString( oSpeaker, DLG_PAGE_ID, page ); DeleteLocalInt( oSpeaker, DLG_HAS_PREV ); DeleteLocalInt( oSpeaker, DLG_HAS_NEXT ); DeleteLocalInt( oSpeaker, DLG_START_ENTRY ); } // Returns a page string that the scripts can use to track // progress through the conversation. string GetDlgPageString() { return( GetLocalString( GetPcDlgSpeaker(), DLG_PAGE_ID ) ); } // Sets a page string that the scripts can use to track // progress through the conversation. void SetDlgPageInt( int page ) { SetLocalInt( GetPcDlgSpeaker(), DLG_PAGE_ID, page ); //DeleteLocalInt( GetPcDlgSpeaker(), DLG_HAS_PREV ); //DeleteLocalInt( GetPcDlgSpeaker(), DLG_HAS_NEXT ); //DeleteLocalInt( GetPcDlgSpeaker(), DLG_START_ENTRY ); } // Returns a page string that the scripts can use to track // progress through the conversation. int GetDlgPageInt() { return( GetLocalInt( GetPcDlgSpeaker(), DLG_PAGE_ID ) ); } // Called to initiate a conversation programmatically between // the dialog source and the object to converse with. If // dlgHandler is "" then the object's default script will be used. void StartDlg( object oPC, object oObjectToConverseWith, string dlgHandler = "", int bPrivate = FALSE, int bPlayHello = TRUE, int bZoom = TRUE ) { // Setup the conversation if( dlgHandler != "" ) SetLocalString( oPC, DLG_CURRENT_HANDLER, dlgHandler ); if( GetObjectType( oObjectToConverseWith ) == OBJECT_TYPE_ITEM ) { // We presume that only one player can talk to an item at // a time... we could check, but we don't. SetLocalObject( oObjectToConverseWith, DLG_ITEM_CONVERSER, oPC ); // We can't actually talk to items so we fudge it. SetLocalObject( oPC, DLG_ITEM, oObjectToConverseWith ); oObjectToConverseWith = oPC; } string dlg = "zdlg_converse"; if( !bZoom ) dlg = "zdlg_converse_nz"; AssignCommand( oObjectToConverseWith, ActionStartConversation( oPC, dlg, bPrivate, bPlayHello ) ); } // Ends the current conversation and will fire the DLG_END event. void EndDlg() { object oSpeaker = GetPcDlgSpeaker(); SetLocalInt( oSpeaker, DLG_STATE, DLG_STATE_ENDED ); } // Returns the number of responses that will be displayed // in the dialog when talking to the specified speaker. // The speaker can be specified for looping optimization // so that the functions don't have to retrieve it every time. int GetDlgResponseCount( object oSpeaker ) { object oHolder = GetLocalObject( oSpeaker, DLG_RESPONSE_LIST_HOLDER ); string listId = GetLocalString( oSpeaker, DLG_RESPONSE_LIST ); return( GetElementCount( listId, oHolder ) ); } // Returns the response string for the specified entry. // The speaker can be specified for looping optimization // so that the functions don't have to retrieve it every time. string GetDlgResponse( int num, object oSpeaker ) { object oHolder = GetLocalObject( oSpeaker, DLG_RESPONSE_LIST_HOLDER ); string listId = GetLocalString( oSpeaker, DLG_RESPONSE_LIST ); return( GetStringElement( num, listId, oHolder ) ); } // Sets up the previous/next buttons void _SetDlgPreviousNext( object oSpeaker, int hasPrevious, int hasNext ) { SetLocalInt( oSpeaker, DLG_HAS_PREV, hasPrevious ); SetLocalInt( oSpeaker, DLG_HAS_NEXT, hasNext ); } // Returns true if the "previous" entry is turned on in the // response list int _HasDlgPrevious( object oSpeaker ) { return( GetLocalInt( oSpeaker, DLG_HAS_PREV ) ); } // Returns true if the "next" entry is turned on in the // response list int _HasDlgNext( object oSpeaker ) { return( GetLocalInt( oSpeaker, DLG_HAS_NEXT ) ); } // Returns true if the "end" entry is turned on in the // response list int _HasDlgEnd( object oSpeaker ) { return( GetLocalInt( oSpeaker, DLG_HAS_END ) ); } // Sets the starting entry for when a response list is // broken into multiple pages. void _SetDlgFirstResponse( object oSpeaker, int start ) { SetLocalInt( oSpeaker, DLG_START_ENTRY, start ); } // Returns the starting entry for when a response list is // broken into multiple pages. int _GetDlgFirstResponse( object oSpeaker ) { return( GetLocalInt( oSpeaker, DLG_START_ENTRY ) ); } // Sets the token for the response string and returns true // if there is a valid response entry for the specified num. int _SetupDlgResponse( int num, object oSpeaker ) { int hasNext = _HasDlgNext( oSpeaker ); int hasPrev = _HasDlgPrevious( oSpeaker ); int hasEnd = _HasDlgEnd( oSpeaker ); if( (hasNext || hasPrev || hasEnd) && num >= 10 ) { if( hasNext && num == 10 ) { SetCustomToken( DLG_BASE_TOKEN + 11, "Next" ); return( TRUE ); } if( hasPrev && num == 11 ) { SetCustomToken( DLG_BASE_TOKEN + 12, "Previous" ); return( TRUE ); } if( hasEnd && num == 12 ) { SetCustomToken( DLG_BASE_TOKEN + 13, "End" ); return( TRUE ); } return( FALSE ); } int i = _GetDlgFirstResponse( oSpeaker ) + num; int count = GetDlgResponseCount( oSpeaker ); if( i < count ) { string response = GetDlgResponse( i, oSpeaker ); SetCustomToken( DLG_BASE_TOKEN + num + 1, response ); return( TRUE ); } return( FALSE ); } // Called to clean-up the current conversation related // resources. void _CleanupDlg( object oSpeaker ) { // See if the PC was associated with an item object oItem = GetLocalObject( oSpeaker, DLG_ITEM ); if( oItem != OBJECT_INVALID ) { DeleteLocalObject( oItem, DLG_ITEM_CONVERSER ); } DeleteLocalInt( oSpeaker, DLG_STATE ); DeleteLocalObject( oSpeaker, DLG_RESPONSE_LIST_HOLDER ); DeleteLocalString( oSpeaker, DLG_RESPONSE_LIST ); DeleteLocalString( oSpeaker, DLG_PROMPT ); DeleteLocalString( oSpeaker, DLG_CURRENT_HANDLER ); DeleteLocalInt( oSpeaker, DLG_PAGE_ID ); DeleteLocalString( oSpeaker, DLG_PAGE_ID ); DeleteLocalObject( oSpeaker, DLG_ITEM ); DeleteLocalInt( oSpeaker, DLG_HAS_PREV ); DeleteLocalInt( oSpeaker, DLG_HAS_NEXT ); DeleteLocalInt( oSpeaker, DLG_HAS_END ); DeleteLocalInt( oSpeaker, DLG_START_ENTRY ); } // Sends the specified dialog event to the specified NPC // using the current script handler. The selection parameter // is used for select events. The speaker is provided for // event specific paramaters to be stored onto. void _SendDlgEvent( object oSpeaker, int dlgEvent, int selection = -1, object oNPC = OBJECT_SELF ) { string dlg = GetCurrentDlgHandlerScript(); if( oNPC == oSpeaker ) oNPC = GetLocalObject( oSpeaker, DLG_ITEM ); SetLocalInt( oSpeaker, DLG_EVENT_TYPE, dlgEvent ); SetLocalInt( oSpeaker, DLG_EVENT_SELECTION, selection ); ExecuteScript( dlg, oNPC ); DeleteLocalInt( oSpeaker, DLG_EVENT_TYPE ); DeleteLocalInt( oSpeaker, DLG_EVENT_SELECTION ); } void _DoDlgSelection( object oSpeaker, int selection, object oNPC = OBJECT_SELF ) { // Check to see if this is one or our internal events int first = _GetDlgFirstResponse( oSpeaker ); switch( selection ) { case 10: if( _HasDlgNext( oSpeaker )) { // Next page _SetDlgFirstResponse( oSpeaker, first + 10 ); return; } break; case 11: if( _HasDlgPrevious( oSpeaker )) { // Previous page _SetDlgFirstResponse( oSpeaker, first - 10 ); return; } break; case 12: if( _HasDlgEnd( oSpeaker)) { // End dialog EndDlg(); return; } break; } selection += first; _SendDlgEvent( oSpeaker, DLG_SELECTION, selection, oNPC ); } // Returns the current conversation state. int _GetDlgState( object oSpeaker ) { return( GetLocalInt( oSpeaker, DLG_STATE ) ); } // Called by the dialog internals to initialize the page // and possibly the conversation void _InitializePage( object oSpeaker, object oNPC = OBJECT_SELF ) { int state = GetLocalInt( oSpeaker, DLG_STATE ); string dlg = GetCurrentDlgHandlerScript(); if( oNPC == oSpeaker ) oNPC = GetLocalObject( oSpeaker, DLG_ITEM ); // See if the NPC has a dialog file defined if( dlg == "" ) { // Try to see if they have a default defined dlg = GetDefaultDlgHandlerScript( oNPC ); SetCurrentDlgHandlerScript( dlg ); state = 0; } // If we aren't initialized if( state == DLG_STATE_INIT ) { // Then we'll send the conversation init event _SendDlgEvent( oSpeaker, DLG_INIT, -1, oNPC ); SetLocalInt( oSpeaker, DLG_STATE, DLG_STATE_RUNNING ); } // Send the page initialization event _SendDlgEvent( oSpeaker, DLG_PAGE_INIT, -1, oNPC ); } /* //I keep this here to uncomment when compiling to test for //errors that can't be found otherwise. Just ignore it. void main() { }*/