! --------------------------------------------------------------------
! GTALK.H : A Library File for Conversation Menus
! Version 3.00
! by Greg Boettcher, Krister Fundin, and Mark J. Tilford
! This library is freeware. You may use it for any purpose without
! permission, but please credit us.
! --------------------------------------------------------------------
!
! -----------
! Description
! -----------
!
! This is an Inform library file designed to handle Photopia-style
! conversation menus, sort of like Phtalkoo.h, but it has certain
! features Phtalkoo.h lacks:
!   * Can do looping/repeating conversations, a la "Being Andrew
!     Plotkin" or "The Cabal."
!   * Can do submenus, menu mazes, etc. Compile Gtalk.inf to see what
!     I mean.
!   * Allows more readable code than Phtalkoo.h, in my humble opinion,
!     since, unlike Phtalkoo.h, it allows quip/response pairs to be
!     right next to each other in your code.
!   * Allows you to turn on/off the "0 to say nothing" option.
!   * Allows you to turn on/off the "What would you like to say?"
!     message.
!
! ---------------
! Version History
! ---------------
!
! 3.00: 2006-07-29 Numerous changes, detailed below, the work of
!                  Krister Fundin. E.g., lots of comments have been
!                  added, and lots of code reformatted. Also, the
!                  speed of performance has been increased, reportedly
!                  producing a measurable improvement on slower
!                  computers.
! 2.04: 2006-05-06 It's now possible to modify a conversation menu
!                  with a character before you've initiated a
!                  conversation with that character, based on work by
!                  Victor Gijsbers. In gtalk.inf, a new bug fix
!                  prevents things like, "The weeds doesn't reply."
! 2.03: 2005-09-06 New features for non-English authors: modifications
!                  by Victor Gijsbers.
! 2.02: 2005-02-08 Optimized a few things for speed and memory
!                  efficiency. Fixed a couple of minor flaws in the
!                  documentation. Rewrote Note #5.
! 2.01: 2005-02-02 Added documentation for the new changes.
! 2.00: 2005-01-29 Major improvements added, as specified below,
!                  mostly the work of Mark J. Tilford. (Not a public
!                  release.)
! 1.01: 2004-09-23 Original release by Greg Boettcher.
!
! ---------------
! Acknowledgments
! ---------------
!
! * This library owes quite a bit to Phtalkoo.h, which was written by
!   David Glasser and was based on Adam Cadre's source code for
!   Photopia. (I pulled the bitwise stuff from there and got my basic
!   start from there, although most of the code here is original.
! * Special thanks to Roger Firth, Andrew Plotkin, and Cedric Knight
!   for helping me with some complex array issues for a previous,
!   unreleased version of this library.
! * And to J. Robinson Wheeler, for showing me his Phtalkoo.h
!   modifications.
! * And to Victor Gijsbers, who made international improvements for
!   version 2.03, and also helped improve 2.04 so that it is now
!   possible to modify a conversation menu with a character before
!   you've initiated a conversation with that character.
! * Big thanks to Mark J. Tilford, who made the improvements resulting
!   in version 2.00, making the syntax more flexible, adding Glulx
!   support, and making other improvements.
! * And big thanks to Krister Fundin, who made the improvements
!   resulting in version 3.00, adding comments, optimizing things,
!   improving performance speed, etc. Krister also previously made
!   some suggestions to improve memory efficiency.
!
! ---------------------------
! Before You Use This Library
! ---------------------------
!
! First download Gtalk.inf if you don't already have it, and make sure
! it's the same version as this file, Gtalk.h! Gtalk.inf is a demo
! game using this library (Gtalk.h), and this library is meant to be
! learned in conjunction with that demo. You should be able to get the
! demo from the same place where you got this file.
!
! -----------------------
! How to Use This Library
! -----------------------
!
! 1. Insert the line:
!         Include "Gtalk";
!    somewhere early in your file. (I included it right after
!    VerbLib.)
! 2. Each "talk-to-able" NPC in your game must be of class Character,
!    and must contain a "quip" routine.
! 3. The "quip" routine consists of one big "switch" clause, like so:
!         Character NPC_ID "Name" someroom
!           with
!           ...
!           quip [a b;
!             switch (a) {
!               ...
!               ! Quip #20
!               201: "~Could I have that apple?~";
!               202: move apple to player;
!                    "~Sure, why not. Say, you're not from
!                      around here, are you?~";
!               203: qtype = [MainMenu or SubMenu];
!                    qqon  = true;
!                    killz = true;
!                    killq = true;
!                    qtransfer = [quip number];
!               204: ! Conversation options to follow
!                    return Qlist(b, 2,   ! 2 quips in
!                                         ! this "array"
!                                    21,  ! Quip #21
!                                    22); ! Quip #22
!               ...
!             }
!             rfalse;
!           ];
!    This sequence -- 201, 202, 203, and 204 -- collectively
!    represents Quip #20, a sample conversation option. (Well, not
!    really. This quip contains every possible element that a quip can
!    have. No real quip would ever do that.)
! 4. Each conversation menu option -- each quip -- can contain any of
!    the following elements:
!         ### = The quip's ID number. Mandatory. Note: Do *not* use a
!               Quip #0! See Note #3 below.
!         ###1: Name: The quip's name, as shown among the list of
!               conversation menu options.
!         ###2: Reply: The reply, including a textual response, and
!               maybe also additional programming statements.
!         ###3: Additional options. Choose from among the following:
!           qtype = MainMenu; If the quip is a main menu.
!                 = SubMenu;  If the quip is a submenu.
!           qqon  = true;     Quip is turned on from the beginning of
!                             the game. (See also Note #6 below.)
!                 = false;    Quip is turned off from the beginning of
!                             the game.
!           killz = true;     To get rid of the "0 to say nothing"
!                             option for any quip. (killz = "kill
!                             zero")
!           killq = true;     To get rid of "What would you like to
!                             say?" for any quip. (killq = "kill
!                             question")
!           qtransfer =       To "go back to the main menu" or any
!              [a quip ID];   other previously defined set of options.
!                             Give the ID of the quip whose
!                             conversation menu options list (that is,
!                             whose ###4 clause) you want to use.
!         ###4: A list of conversation menu options that can be chosen
!               *after* this quip. See Step 5 below.
!    Note that none of these four elements is mandatory. For examples,
!    consult Gtalk.inf.
! 5. With version 2.01 and up of Gtalk.h, the syntax for defining
!    conversation options is considerably easier than it was before.
!    However, it still requires some learning. For example, suppose
!    you have a Quip 50, and you want it to provide five options --
!    namely, Quips 51, 52, 53, 54, and 55. You'd do something like
!    this:
!         Character NPC_ID "Name" someroom
!           with
!           ...
!           quip [a b;
!             switch (a) {
!               ...
!               ! Quip #50
!               ...
!               504: return Qlist(b, 5,  ! 5 values in
!                                        ! this "array"
!                                   51,  ! Quip #51
!                                   52,  ! Quip #52
!                                   53,  ! Quip #53
!                                   54,  ! Quip #54
!                                   55); ! Quip #55
!               ...
!             }
!             rfalse;
!           ];
!    As you can see, in ###4 you simply call the Qlist routine, where
!    the first argument is b, the second argument is the total number
!    of conversation options, and the remaining arguments are the ID
!    numbers of the quips in your options list.
!       Note the format here; Quip #55 should be specified as 55, not
!    as 551 or whatever.
!       Also note that your options list should include all the
!    options that will ever be available at the given juncture. If you
!    want a quip's options to change over time, then make sure its
!    ###4 makes reference to all the quip options that it will ever
!    provide; then turn off any quips in the list that aren't valid
!    choices at the beginning of the game.
!       But what if you have more than 5 options? This could be a
!    problem, because conventional Z-machine Inform allows no more
!    than 7 arguments when calling a routine.
!       Unless you're writing for Glulx, here's the best way to define
!    more than 5 menu options:
!          504: return Qlist(b,   11, 51,52,53,54,55) |
!                      Qlist(b-6,     56,57,58,59,60,61);
!    (The | is a "bitwise or" operator and basically serves to make
!    sure that the right value is returned from the right Qlist()
!    call. Just trust me, it works. Or rather, trust Mark, who devised
!    this clever bit of code.)
!       Here's the pattern for when you have more than 11 options:
!          504: return Qlist(b,    40, 51,52,53,54,55) |
!                      Qlist(b-6,      56,57,58,59,60,61) |
!                      Qlist(b-12,     62,63,64,65,66,67) |
!                      Qlist(b-18,     68,69,70,71,72,73) |
!                      Qlist(b-24,     74,75,76,77,78,79) |
!                      Qlist(b-30,     80,81,82,83,84,85) |
!                      Qlist(b-36,     86,87,88,89,90);
!    See also Gtalk.inf, Quip #70.
!       On the other hand, if you're writing for Glulx, the syntax is
!    much simpler:
!          504: return Qlist(b, 40, 51,52,53,54,55,56,57,58,59,60,
!                                   61,62,63,64,65,66,67,68,69,70,
!                                   71,72,73,74,75,76,77,78,79,80,
!                                   81,82,83,84,85,86,87,88,89,90);
!    I have been informed that Glulx has the capacity for any number
!    of arguments.
!       In case anybody's interested, there is also another option:
!          504: switch (b) {
!                  0: return 11;  ! 11 options
!                  1: return 51;  ! Quip #51
!                  2: return 52;  ! Quip #52
!                  3: return 53;  ! Quip #53
!                  4: return 54;  ! Quip #54
!                  5: return 55;  ! Quip #55
!                  6: return 56;  ! Quip #56
!                  7: return 57;  ! Quip #57
!                  8: return 58;  ! Quip #58
!                  9: return 59;  ! Quip #59
!                 10: return 60;  ! Quip #60
!                 11: return 61;  ! Quip #61
!               }
!    If you find this way easier, then feel free to use it, but it
!    doesn't seem easier to me. This is the old, clumsy way, which is
!    no longer necessary with Gtalk.h 2.x.
! 6. Each character must have (at least) one conversation menu option
!    that represents the "main menu." It is this that's called into
!    action first. (To find out how, read Step 8 below.) This main
!    menu need not (should not) have a ###1 (option name) or a ###2
!    (reply), but it must have a ###4 menu options list, plus a ###3
!    with "qtype = MainMenu;". (To find out why, read Note #2 below.)
! 7. If you have any submenus, your submenu quips should have a ###3
!    with "qtype = SubMenu;". To find out why, read Note #2 below.
! 8. Each character has a "select" property routine. This is what you
!    must call to set the conversation menu in motion. Call
!    "self.select(X);" or "CharacterID.select(X);", where X is the
!    Quip ID of your main menu.
! 9. It's unlikely that you'll want the choices in your conversation
!    menus to always remain the same.
!      - To turn Quip #22 on:   self.qon(22);
!      - To turn Quip #22 off:  self.qoff(22);
!      - To turn on a series    self.qon(1,2,3,4,5);
!          of quips (up to 5):
!      - To turn off a series   self.qoff(1,2,3,4,5);
!          of quips (up to 5):
!      - To deal with a series  self.qset(1,0, 2,1);
!          of quips, turning              ! Quip #1 on
!          some on, others off.           ! Quip #2 off
!          (Only up to 2 quips
!          with Z-code Inform;
!          unlimited with Glulx)
!      - To test a quip:
!              self.qtest(22) is true  if Quip #22 is on
!                             is false if Quip #22 is off
!      - Or, if you're        ObjectID.qon(22);
!        calling from         ObjectID.qoff(22);
!        a different part     ObjectID.qon(1,2,3,4,5);
!        of your code:        ObjectID.qoff(1,2,3,4,5);
!                             flag = ObjectID.qtest(22);
!    And remember to use "###3: qqon = true;" for quips that are to be
!    turned on from the beginning. Otherwise, all quips are initially
!    turned off.
!10. Gtalk.h does not deal with any verb issues; you must do that on
!    your own. Here are some tips.
!      Verb Tip 1:
!        Typically, games with conversation menus utilize the TALK
!        verb rather than ASK and TELL. So you may wish to define the
!        verb Talk. Then, for each of your NPCs, use the "before"
!        routine to intercept this verb (or whatever verb(s) you
!        choose), and then call the NPC's "select" routine as
!        described in Step 8.
!      Verb Tip 2:
!        You may also wish to disable the standard conversation verbs.
!          * You can do this the hard way, as Gtalk.inf does, by
!            disabling the conversation verbs one by one -- namely:
!               Answer/Say/Shout/Speak
!               Ask
!               No
!               Sorry
!               Tell
!               Yes/Y
!            You may also wish to intercept "Order" and "Answer" in
!            the "life" property of each of your NPCs, disabling that
!            as well. ("Order" handles things like "HARRY, GO NORTH";
!            "Answer" handles things like "HARRY, HELLO" or "HARRY,
!            ONIONS".) For more info, consult Gtalk.inf.
!          * Just before releasing this library I thought of an easier
!            way to do this. Between Parser and VerbLib you could
!            include:
!               Include "Parser";
!               Object LibraryMessages
!                 with before [;
!                   Answer, Ask, No, Orders,
!                   Sorry, Tell, Yes:
!                     "To talk to someone,
!                       please type TALK TO PERSON.";
!                 ];
!               Include "VerbLib";
!            However, I haven't tested this, so I leave this for you
!            to experiment with.
!      Verb Tip 3:
!        I also recommend one more finishing touch: do an
!        "Extend only 'speak' replace", making it so that it so that
!        'speak' (but not 'answer', 'say', or or 'shout') becomes
!        synonymous with 'talk'. Consult Gtalk.inf to see what I mean.
!11. See also the following notes.
!
! Note #1: Gtalk.h has an optional feature where it can routinely look
! for any quip whose options are all turned off, and turn that quip
! off for you. To turn on this feature, simply define the constant
! AutoDeactivate just before you include Gtalk.h. See Gtalk.inf, which
! does this. (This feature is not on by default because it might be
! unwanted, and could theoretically slow games down, although this is
! probably not a big factor.)
!
! Note #2: What, really, is the difference between MainMenu quips,
! SubMenu quips, and normal quips?
!   * MainMenu quips (unlike SubMenu quips and normal quips) will
!     print "You can't think of anything to say" when all of their
!     options are turned off.
!   * Having reached a MainMenu or SubMenu quip (as opposed to a
!     normal quip) does not constitute actually having said anything.
!     Gtalk.h takes this into account when deciding whether it makes
!     sense to print the message "You decide not to say anything after
!     all."
!
! Note #3: Do NOT use a Quip #0. Just don't. Many of the routines in
! gtalk.h (qon, qoff, qset, and qtransfer) would fail if they
! attempted to deal with a Quip #0, and beginning with version 3.x
! gtalk.h doesn't even attempt to process any quips numbered 0.
!
! Note #4: Also, don't use negative numbers for Quip IDs. They won't
! work as you'd expect. Just use Quip IDs from 1 to 65535, rather
! than -32768 to 32767. It's a lot easier.
!
! Note #5: The library is presently set up so that you can have quips
! numbered 1 to 95 when writing for the Z-machine, or 1 to 191 quips
! per NPC when writing for Glulx. This can be changed, and sometimes
! should be changed, because it affects both your game's memory
! consumption and the speed at which Gtalk.h runs.
!    Let's see how this works by observing the following bit of code:
!       Class Character
!         has  animate,
!         with qflag 0 0 0 0 0 0,
!         !   For the    For
!         ! Z-machine: Glulx:
!         !         6      6 elements in the "qflag" word array
!         !     *   2  *   4 bytes per word
!         !     *   8  *   8 bits per byte
!         !     =  96  = 192
!         !     -   1  -   1 because it's 0-95 or 0-191
!         !     =  95  = 191 highest quip value for each NPC
!         maxquip [;
!           return self.#qflag * 8 - 1;
!         ],
! As you can see, the highest quip value for each NPC, and thus the
! maximum number of quips for each NPC, depends on the Character.qflag
! array. There are normally six elements (six 0's) in the array.
! Multiply by 16 as indicated above (or 32 if you're writing for
! Glulx) and subtract 1, and you get a maximum of 95 quips per NPC
! with the Z-machine (or 191 with Glulx).
!    You can, of course, override the Character class's qflag array.
! For any of your NPCs, simply put a qflag array into the NPC's object
! definition, and put in as many elements as you want to produce the
! desired maximum number of quips, as follows:
!       Number of
!        elements
!        in qflag   Highest quip value for each NPC:
!          array:   (Z-machine)  (Glulx)
!               1   *16-1=  15   *32-1=  31
!               2   *16-1=  31   *32-1=  63
!               3   *16-1=  47   *32-1=  95
!               4   *16-1=  63   *32-1= 127
!               5   *16-1=  79   *32-1= 159
!               6   *16-1=  95   *32-1= 191
!               7   *16-1= 111   *32-1= 223
!               8   *16-1= 127   *32-1= 255
!               9   *16-1= 143   *32-1= 287
!              10   *16-1= 159   *32-1= 319
!             etc.
!    The only restriction is, if you're writing for the Z-machine, you
! can't use more than 6553 quips for any NPC. (Why 6553? Inform's
! integer limits of -32768 to 32767 can also be represented as 0 to
! 65535. Divide by ten, and you get 6553.) It's doubtful that anyone
! would need more than 6553 quips for any one NPC.
!
! Note #6: Gtalk.h now has an optional feature where you can make it
! so all the quips are automatically turned on from the beginning of
! the game unless their ###3 clause specifically says "qqon = false;".
! To do this, simply define the constant QuipsOnByDefault, like so...
!      Constant QuipsOnByDefault;
! ...just before you include Gtalk.h. Gtalk.inf doesn't do this, but I
! think you can figure out how to do this on your own.
!
! Note #7: Thanks to Victor Gijsbers, Gtalk now makes things easier
! for authors not writing in English. If your game uses a language
! other than English, make sure to define the constants GT_SELECT,
! GT_ZEROEXIT, GT_WOULDLIKE, GT_NOQUIP, and GT_NOSAY, setting them to
! strings that are appropriate translations of the English expressions
! of those constants (see below). If you are writing in English, all
! you have to do is make sure that the constant EnglishNaturalLanguage
! is defined, as English.h does automatically. Thanks again to Victor
! Gijsbers for this modification.
!
! -----------------------------------------------
! Differences Between Versions 1.x and 2.x and 3.x
! -----------------------------------------------
!
! It is not necessary to read this section in order to use gtalk.h. It
! is mostly here for my own reference, and also for anyone else who is
! interested in knowing what modifications have been done to this
! library. For a more concise version history, see above.
!
! Changes new to version 2.x (mostly due to the work of Mark J.
! Tilford):
!   * You no longer have to define your conversation menu options with
!     a convoluted array-like routine, but can use a much easier
!     syntax. This eliminates what was formerly Gtalk.h's biggest
!     disadvantage, so I am grateful to Mark for helping with this.
!   * Glulx support has been added.
!   * Gtalk.h is now more efficient and faster, perhaps even faster
!     enough for some people to notice the difference.
!   * You can now declare a constant QuipsOnByDefault, to make your
!     conversation options on by default.
!   * The Character.qset() routine is now more flexible.
!   * Starting with version 2.03, Victor Gijsbers has made some
!     modifications to make things easier for authors not writing
!     games in English. See Note #7.
!
! Changes new to version 3.x (mostly due to the work of
! Krister Fundin):
!
!   * Reformatted code, minor tweaks, some optimizations, lots of
!     comments for the benefit of anyone who wants to mess around with
!     the library and add more features.
!   * Added System_file and made the string constants into #Defaults,
!     so that they can be individually replaced even in English games.
!   * Added two new strings (GT_OPTIONPREFIX and GT_OPTIONSUFFIX),
!     which can be used to change the style of the option listing,
!     e.g. into:
!        1. Say this
!              or
!        #2 Say that
!              or
!        Type 3 to say something else
!              etc.
!   * Some Character methods that were only used internally have been
!     made into functions instead, since that means fewer properties
!     and less dynamic memory used.
!   * Moved AutoDeactivate behaviour to a separate function in order
!     to reduce clutter a bit
!   * Calculate maxquip only once, in qinitial (now called initquips).
!   * Made warnings for too high quip numbers a bit more elaborate,
!     but they are only compiled when the DEBUG switch is set.
!   * Ditched warnings for odd number of arguments to qset, since that
!     doesn't seem like a mistake that should happen very easily.
!   * Changed the PowersOfTwo function into a table, for efficiency.
!   * Used qset directly as much as possible.
!   * Quip 0 is not allowed, so skip looping through it
!   * In Gtalk.inf, an "unweary" verb has been added, to test the
!     consequences of  changing the on/off status of quips prior to
!     initiating a conversation.


System_file;


! Define default versions of all strings that this extension uses, so
! that they can be replaced, E.G. when developing a game in a language
! other than English.
#default GT_SELECT       = "Select an option ";
#default GT_ZEROEXIT     = "or 0 to exit ";
#default GT_WOULDLIKE    = "What would you like to say?^";
#default GT_NOQUIP       = "You can't think of anything to say.^";
#default GT_NOSAY        = "You decide not to say anything after all.^";
#default GT_OPTIONPREFIX = "(";
#default GT_OPTIONSUFFIX = ") ";


#ifndef WORDSIZE;

Constant TARGET_ZCODE;
Constant WORDSIZE = 2;

#endif;


! This array of two-powers is used in order to save some time when
! processing quips.
Array GT_Powers
-> $$000000001
   $$000000010
   $$000000100
   $$000001000
   $$000010000
   $$000100000
   $$001000000
   $$010000000;

! The five global variables that can be set in a quip's ###3 clause.
Global qtype;
Global qqon;
Global qtransfer;
Global killz;
Global killq;

! These values can be used for qtype in a quip's ###3 clause.
Constant MainMenu = 1;
Constant SubMenu  = 2;


! The Qlist function is used as a shorthand to defining options in a
! quip's ###4 clause. We define separate Z-code and Glulx versions,
! since the latter benefits from being able to accept any number of
! arguments.

#ifdef TARGET_ZCODE;

[ Qlist num a1 a2 a3 a4 a5 a6;
  switch (num)
  {
     0: return a1;
     1: return a2;
     2: return a3;
     3: return a4;
     4: return a5;
     5: return a6;
  }
  return 0;
];

#ifnot;

[ Qlist _vararg_count n t;
  @copy sp n;
  _vararg_count--;
  if (n < 0 || n >= _vararg_count)
  {
     return 0;
  }
  while (n > 0)
  {
     n--;
     @copy sp t;
  }
  @copy sp t;
  return t;
];

#endif;


! The Character class. All NPCs with conversation menus should belong to
! this class.
Class Character
  has  animate,

  ! This is the array in which we store the on/off status of all our quips.
  ! The number of entries determines how many quips we can have at most,
  ! and this can be calculated like so:
  !
  !   For the    For
  ! Z-machine: Glulx:
  !         6      6 elements in the default "qflag" word array
  !     *   2  *   4 bytes per word
  !     *   8  *   8 bits per byte
  !     =  96  = 192
  !     -   1  -   1 because it's 0-95 or 0-191
  !     =  95  = 191 highest quip value for each NPC
  !
  ! If more quips are needed, override this property and add more zeroes
  ! to the array.
  with qflag 0 0 0 0 0 0,

  ! the number of our highest quip will be stored here, so that we only
  ! have to calculate it once
  maxquip 0,

  ! This method carries out a conversation with our character. Call it with
  ! the first argument set to a quip which represents a main menu.
  select
  [ curquip times quipnum numoptions onoptions o selected spoken;

     ! Check if we need to initialize. This process is, among other things,
     ! responsible for calculating our maxquip value, so we can see if it
     ! has been done by checking if maxquip is still set to zero.
     if (self.maxquip == 0)
        initquips(self);

     ! do a big loop, because this quip could be only the first of many in
     ! an extended conversation
     for (times = 1 : : times++)
     {
        ! turn off quips with no options on, if desired
        #ifdef AutoDeactivate;
            AutoDeactivateQuips(self);
        #endif;

        ! Reset global variables. Note that we don't care about qqon, since
        ! it's only used during the initquips() stage.
        qtype = 0;
        qtransfer = curquip;
        killz = 0;
        killq = 0;

        ! ask the current quip to set the five global variables (qtype,
        ! qqon, qtransfer, killz and killq) to their intended values
        self.quip(curquip * 10 + 3);

        ! if this quip is not a menu of some kind, note that that player
        ! has said something
        if (qtype ~= MainMenu && qtype ~= SubMenu)
           spoken = true;

        ! print the reply for this quip
        self.quip(curquip * 10 + 2);

        ! The next step is to see if we have any options to display. Assume
        ! that we don't.
        onoptions = false;

        ! Get the quip argument for the option list.  Note that qtransfer
        ! will be set either to the current quip or to a different quip,
        ! but always to SOME quip
        quipnum = qtransfer * 10 + 4;

        ! get the total number of options, whether on or off
        numoptions = self.quip(quipnum, 0);

        ! if we have any options at all, see if any of them are on
        for (o = 1 : o <= numoptions : o++)
        {
           ! we don't care about how many options there are right now, so
           ! leave this loop as soon as we find at least one option that
           ! is on
           if (self.qtest(self.quip(quipnum, o)))
           {
              onoptions = true;
              break;
           }
        }

        if (~~onoptions)
        {
           ! There were no options available, hence nothing for the player
           ! to say, so this conversation is finished. If this was a main
           ! menu, report that the player can't think of anything to say,
           ! otherwise assume that something appropriate has already been
           ! printed in our reply.
           if (qtype == MainMenu)
              print (string) GT_NOQUIP;

           return;
        }

        ! We have options. Display an extra prompt before printing them, if
        ! desired
        if (~~killq)
        {
           ! unless this is the first time through, print an extra line
           ! feed so that we're separated from the previous reply
           if (times > 1)
              new_line;

           print (string) GT_WOULDLIKE;
        }

        ! Go through the options again, now printing a list of those that
        ! are on. Note that we can reuse the variables quipnum and
        ! numoptions from the previous iteration.
        for (onoptions = 0, o = 1 : o <= numoptions : o++)
        {
           ! get this quip
           curquip = self.quip(quipnum, o);

           ! see if it's on
           if (self.qtest(curquip))
           {
              ! print the number of this option (not the internal number,
              ! of course, but one that starts at 1 and increases for every
              ! option that we display)
              print (string) GT_OPTIONPREFIX,
                    ++onoptions,
                    (string) GT_OPTIONSUFFIX;

              ! print the option text
              self.quip(curquip * 10 + 1);
           }
        }

        ! separate the options from the prompt with an empty line
        new_line;

        ! Now get the response from the player. Keep on asking until we get
        ! an acceptable answer.
        do
        {
           ! print the basic prompt
           print (string) GT_SELECT;

           ! if the player can exit the conversation by typing 0, mention
           ! this as well
           if (~~killz)
              print (string) GT_ZEROEXIT;

           print ">> ";

           ! read a line of input
           KeyboardPrimitive(buffer, parse);

           ! Check for empty input, otherwise for a number. In any case,
           ! invalid input will set selected to something < 0.
           if (parse->(WORDSIZE - 1) == 0)
              selected = -1;
           else
              selected = TryNumber(1);

           ! disallow 0, if desired
           if (killz && selected == 0)
              selected = -1;

        } until (selected >= 0 && selected <= onoptions);

        ! check for 0, which means that we're leaving this conversation
        if (selected == 0)
        {
           ! print a notice saying that the PC decided not to say anything
           ! after all, but only if nothing has actually been said so far
           if (~~spoken)
              print (string) GT_NOSAY;

           ! return from this method
           return;
        }

        ! go through the options once again, this time to retrieve the
        ! option that the player selected, and thus the quip that we want
        ! to process next
        for (onoptions = 0, o = 1 : o <= numoptions : o++)
        {
           ! get this quip
           curquip = self.quip(quipnum, o);

           ! see if it's on
           if (self.qtest(curquip))
           {
              ! it's on, but is it the one the player wants?
              if (selected == ++onoptions)
              {
                 ! It's the right one. Just break out of this loop, since
                 ! we'll then jump back to the beginning with curquip set
                 ! to the quip that the player selected.
                 break;
              }
           }
        }
     }
  ],

  ! The following three methods are a bit limited in their use, since the
  ! Z-machine has a fixed limit on the number of arguments that can be
  ! given to a method. If we are compiling for Glulx, these will be
  ! replaced (at run-time) with better versions defined further on.

  ! turn at most two quips either on or off
  qset
  [ a b c d;
     qset_(self, a, b);
     qset_(self, c, d);
  ],

  ! Turn on at most five quips. The funky syntax here just means that if an
  ! argument is empty, then we don't bother checking those that follow
  qon
  [ a b c d e;
     if (a) { qset_(self, a, 1);
     if (b) { qset_(self, b, 1);
     if (c) { qset_(self, c, 1);
     if (d) { qset_(self, d, 1);
     if (e) { qset_(self, e, 1); }}}}}
  ],

  ! turn off at most five quips
  qoff
  [ a b c d e;
     if (a) { qset_(self, a, 0);
     if (b) { qset_(self, b, 0);
     if (c) { qset_(self, c, 0);
     if (d) { qset_(self, d, 0);
     if (e) { qset_(self, e, 0); }}}}}
  ],

  ! test a given quip to see if it is on or off
  qtest
  [ qp byte bits;
     ! check for too high quips, but only if we're compiling for debugging
     #ifdef DEBUG;
        if (qp > self.maxquip)
        {
           print "Gtalk: tried to test quip ", qp, " of ", (name) self,
                 ", but the highest available quip is ", self.maxquip,
                 ". Refer to the documentation on how to enlarge the qflag
                 array.^";
           rfalse;
        }
     #endif;

     ! Break down the quip number into bytes and bits. We can't optimize
     ! away the division, since the Z-machine lacks a rotate operator, but
     ! we can at least substitute the modulo for a bit-wise AND.
     byte = qp / 8;
     bits = GT_Powers->(qp & $$111);

     ! return true if the quip is on
     return (self.&qflag->byte & bits == bits);
  ]
;


! Internal function: set a character's quip to a given on/off state.
[ qset_ char qp state byte bits;
  ! In most cases the quips will already be initialized
  ! at this point. But, just in case an author wants to
  ! turn quips on/off before the conversation menu is
  ! called up, initialize the quips here if necessary.
  if (char.maxquip == 0)
     initquips(char);

  ! check for too high quips, but only if we're compiling for debugging
  #ifdef DEBUG;
     if (qp > char.maxquip)
     {
        print "Gtalk: tried to set quip ", qp, " of ", (name) char,
              ", but the highest available quip is ", char.maxquip,
              ". Refer to the documentation on how to enlarge the qflag
              array.^";
        return;
     }
  #endif;

  ! break down the quip number into bytes and bits
  byte = qp / 8;
  bits = GT_Powers->(qp & $$111);

  ! turn the bit in question on or off
  if (state)
     char.&qflag->byte = char.&qflag->byte | bits;
  else
     char.&qflag->byte = char.&qflag->byte & ~bits;
];


! Internal function: do some initialization for a given character.
[ initquips char qp;
  ! use glulx versions of some methods if we can
  #ifdef TARGET_GLULX;
     char.qon  = CharacterGlulx.qon;
     char.qoff = CharacterGlulx.qoff;
     char.qset = CharacterGlulx.qset;
  #endif;

  ! initialize the maxquip value
  char.maxquip = char.#qflag * 8 - 1;

  ! go through all the quips
  for (qp = 1 : qp <= char.maxquip : qp++)
  {
     ! see if we want quips to be on off by default
     #ifdef QuipsOnByDefault;
        qqon = 1;
     #ifnot;
        qqon = 0;
     #endif;

     ! Obtain the desired qqon value for this quip. We'll use the default
     ! unless the quip says otherwise.
     char.quip(qp * 10 + 3);
     if (qqon)
        char.qon(qp);
  }
];


#ifdef TARGET_GLULX;

! Glulx versions of a couple of methods.
Object CharacterGlulx
  with
  qset
  [ _vararg_count qp state;
     while (_vararg_count > 0)
     {
        @copy sp qp;
        @copy sp state;
        qset_(self, qp, state);
        _vararg_count = _vararg_count - 2;
     }
  ],

  qon
  [ _vararg_count qp;
     while (_vararg_count > 0)
     {
        @copy sp qp;
        qset_(self, qp, 1);
        _vararg_count--;
     }
  ],

  qoff
  [ _vararg_count qp;
     while (_vararg_count > 0)
     {
        @copy sp qp;
        qset_(self, qp, 0);
        _vararg_count--;
     }
  ]
;

#endif;


#ifdef AutoDeactivate;

! Internal function: turn off all quips that have options that are all off.
! We only compile and make use of this if requested to.
[ AutoDeactivateQuips char keepgoing qp o quipnum numoptions;
  do
  {
     ! assume that we won't have to repeat this
     keepgoing = false;

     ! go through all quips
     for (qp = 1 : qp <= char.maxquip : qp++)
     {
        ! we don't need to check quips that are already off
        if (char.qtest(qp))
        {
           ! get the option list and the number of options for this quip
           quipnum = qp * 10 + 4;
           numoptions = char.quip(quipnum, 0);

           ! see if we have any options at all
           if (numoptions)
           {
              ! go through the options
              for (o = 1 : o <= numoptions : o++)
              {
                 ! If the quip for this option is on, then we can skip the
                 ! current quip and move on to the next one. (Sorry for the
                 ! spaghetti.)
                 if (char.qtest(char.quip(quipnum, o)))
                    jump nextquip;
              }

              ! We found no options that were on, so turn off this quip.
              ! Since this could affect other quips that have this quips as
              ! one of their options, also note that we have to go through
              ! this loop at least once more.
              qset_(char, qp, 0);
              keepgoing = true;
           }
        }
nextquip;
     }
  } until (~~keepgoing);
];

#endif;