!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
!   Converse     2                      An Inform 6 library to generate 
!                                       Menu-based conversations
!                                       by L. Ross Raszewski
!    V2.0                               rraszews@acm.org
!
! New in this version: *Complete rewrite.  Some incompatabilities with older
!                      versions may occur.
!                      define CONVERSE_1_COMPAT_MODE
!                      to increase version 1 compatability
!                      *Lengthy Rant snipped.
!                      *Upgrade to altmenu 4.3 code base
!
!
! The purpose of this library is to generate extrensible, menu driven
! conversations.  
!
! 
!  So, here's what you do...
!
!  Any NPC you want to "talk to" must be of the class "Conversor"
!  their "conversation" property should hold
!  the name of an object.  this object is the npc's "converse controller"
!  a converse controller is of class (get this) conversation_controller
!
! The children of this object are the conversation topics available for the
! character in question.
! more topics can be added later by moving them to the converse controller.
! (it may be wise to use the pmove function, in order to preserve lineage)
! the class functions .putconv(topic) and .remconv(topic) will add and
! remove the specified topic from the converser PutTopic(topic) will add the
! topic to the converse controller of the person currently being talked to
! ONLY during a conversation, the global Converse_controller is set to the
! current active converse controller.  parent(converse_controller) is
! the NPC being spoken to, only while the conversation is in progress.
! 
!  Then create the topic-objects.  The default format is thus:
!
!   Topic (name) "Short Name"
!       with playerpart "What you want the player to say ie. Asking
!                        the question",
!            convpart "What the other party says.",
!            altplayerpart "What the player says on subsequent selections of
!                           this item",
!            altconvpart "Ditto";
!
! The player will, upon selecting the topic, see the Playerpart, then will 
! be prompted to press a key (You must have the WaitForKey() function),
! then will see the Convpart.  You can circumvent the playerpart/convpart
! bit by making the conversation part of the topic's description property.
!
! Define the array GenTopics--> before inclusion to give a list of topics which
! all Conversors will respond to (eg. "Tell me about yourself") 
! Also, set NGenTop to the number of GenTops 
! "general" topics should be of class "gtopic", all other syntax is the same.
!
! A separator will be added between gentopics and normal converse topics.
!
! To add topics later in the game, send the conversor the command 
!  .putconv(topic_to_be_added);
! To remove a topic:  .remconv(topic_to_be_removed);
!
! However, you cannot remove any topic that was placed in the Inittop
! property.  If you want a starting topic to be removed, putconv it instead.
!
! To put a topic in play while in the menu system, use PutTopic(topic).  
! This will both add the topic to the subject's list of topics, and add 
! it to the conversation menu (using putconv will do this too, but
! to display the new topic, the conversation would have to be restarted).
!
!  CONVERSE__TX holds the text to be printed above the menu (eg, "Ask About:")
!   Define it to something else (eg. CONVERSE__TX="What do you want to say?")
!   before inclusion to change the default.
!
!  Define CONVERSE_NO_GRAMMAR to suppress the grammar additions.
!
!  This library REQUIRES the Altmenu Suite (Altmenu, Domenu)
!  and utility.h

system_file;
Ifndef UTILITY_LIBRARY;
message error "Converse 2.0 requires the Utility Library.";
Endif;
Iffalse ALTMENU_LIBRARY >= 42;
message error "Converse 2.0 requires version 4.2 or greater of the AltMenu Suite.";
Endif;
ifndef CONVERSE_LIBRARY;
Constant CONVERSE_LIBRARY 20;
Default CONVERSE__TX = "Ask About:";
Global ConverseController;

Class Topic_t
        class option,
        with
             description [ i ;
                           if (self hasnt visited) give self visited;
                            else i=1;                           

                           if (i==0 || self.altplayerpart==0)
                            self.playerpart();
                           else self.altplayerpart();
                           Waitforkey("^^^[Press SPACE]^^^");
                           if (i==0 || self.altconvpart==0)
                            return self.convpart();
                           else return self.altconvpart();
                         ],
             altplayerpart 0,
             altconvpart 0,
             playerpart 0,
             convpart 0;
Class Topic class Topic_t;
ifdef GenTopics;
Class GTopic class Topic_t,
    with description [;
                          self.playerpart();
                          Waitforkey("^^^[Press SPACE]^^^");
                          return self.convpart();
                     ];   
endif;

Class Conversation_controller class menu,
        with short_name [ o; o=parent(self); print (name) o ; return 1;],
             description [; print (string) CONVERSE__TX, "^^";  return 2;],
             number 2,
        has concealed;

Class Conversor
        with
                convref 0 0 0 0 0 0 0 0 0 0 0 0,
                putconv [ topc;
                               Pmove(topc, self.conversation);
                        ],
                remconv [ topc; 
                              remove topc;
                        ],
                conversation NULL;

Topic endconv "End Conversation"
        with description [; return 3;];

[ PutTopic i;
  Pmove(i,conversecontroller);
  Pmove(EndConv,ConverseController);
];
[ RunConversation agent i j;
   j=conversecontroller;
   converseController=Agent.conversation;
   move conversecontroller to Agent;
#ifdef GenTopics;
   while (child(ConverseController) ofclass object)
    Pmove(child(conversecontroller),convsep);
   for (i=0:i<=(NGenTop):i++)
    { if (GenTopics-->i ofclass object &&
          GenTopics-->i notin ConverseController)
       pmove(GenTopics-->i,converseController);
    }
   Pmove(ConvSep,converseController);
   while (child(Convsep) ofclass object)
      pmove(child(Convsep),conversecontroller);

#endif;
#ifdef CONVERSE_1_COMPAT_MODE;
  if (Agent provides inittop)
  {
   for (i=0:i<(Agent.#inittop/2):i++)
    { if (Agent.&inittop-->i ofclass object &&
          Agent.&inittop-->i notin ConverseController)
      pmove(Agent.&inittop-->i,ConverseController);
    }
  }
#endif;
   Pmove(Endconv,converseController);
   converseController.select();
   remove converseController;
   converseController=j;
];

[ ConverseSub;
   if (noun==player) return L__M(##Tell,1,noun);
   if (~~(noun ofclass Conversor &&
          noun.conversation ofclass Conversation_Controller))
        return L__M(##Converse);
   RunConversation(noun);
   AfterRoutines();
];  
Separator ConvSep " ";
ifndef CONVERSE_NO_GRAMMAR;
Verb 'talk' 'converse' 'interview'
        *       creature                         ->Converse
        *       'with'/'to' creature             ->Converse;

Extend "ask" replace
        *       creature "about" noun                   ->Show reverse;
Extend "tell" replace
        *        creature "about" noun                  ->Show reverse;
Extend 'show' replace
                * creature noun                  -> Show reverse
                * noun 'to' creature             -> Show;
endif;
endif;
