!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
!   Converse     2                      An Inform 6 library to generate
!                                       Menu-based conversations
!                                       by L. Ross Raszewski
!    V2.0                               [email protected]
!
! 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;