/*
       marry.t -- Inter-character marriage for TADS games.
       Copyright (c) 1997 by Christopher Nebel

       This code may be freely included in any TADS game, commercial
       or otherwise, but is not in the Public Domain.  Please send
       bugs and comments to <[email protected]>.


       INTRODUCTION:

       Ever wanted to write a more mannerly interactive fiction game?
       An adaptation of "Pride and Prejudice," perhaps?  Well, here's
       a small library to help.  Using it, you can allow the player
       to marry other NPCs and even arrange marriages between NPCs.


       INSTRUCTIONS:

       This code was written to work with TADS 2.2 using adv.t, though
       with minor modifications it could work with WorldClass as well.
       Write your actors as usual, include "marry.t", then start
       overriding these two methods:

       - verMarry(d)     : A marriage has been proposed between <self>
                           and <d>.  Speak now, or forever hold your piece.
       - saywillmarry(d) : Performs the actual marriage.

       There's also one useful property, "betrothed".  If a character has
       agreed to marry someone, this property points to the character they're
       married (or at least engaged) to.  If they're single, it will be nil.

       There are several other methods that are defined for internal use:
       verDoMarry(actor), doMarry(actor), marry(d), and saybetrothed.  In
       general, you shouldn't override these, though you might use them
       for special effects.


       MORE DETAILED INSTRUCTIONS:

       - verMarry(d): This is where you determine who will agree to marry whom.
       It covers not only cases where the player is proposing (e.g., "marry Jane"
       or "Jane, marry me"), but also NPCs that you're ordering to marry to each
       other (e.g., "Wickham, marry Lydia.")

       verMarry works like a normal verXoVerb handler (though it isn't really one).
       If there is any output, the proposal is considered to be rejected.  If
       there is no output, program flow continues on to check the other partner
       and then to the "saywillmarry" method.

       By default, no one will agree to marry anyone, the anti-social wretches.
       To change this, override verMarry.  Here's a simple example:

       Jane: Actor
               verMarry(d) = {
                       if (d != Bingley)
                               "\"But I love Mr.\ Bingley!\" ";
               }
       ;

       Now Jane will agree to marry Bingley, but no one else.  However, Bingley
       has his own say in the matter, so for the marriage to work, we have to
       override his verMarry as well:

       Bingley: Actor
               verMarry(d) = {
                       if (d != Jane)
                               "Mr.\ Bingley seems uninterested in marrying <<d.thedesc>>. ";
               }
       ;

       Of course, you are free to represent much more complicated relationships.
       The one caveat is that you should not alter the game state in verMarry,
       since like a verXoVerb method, you will be called invisibly at times.
       Here's a more complex example:

       Elizabeth: Actor
               verMarry(d) = {
                       if (d == Darcy) {
                               if (not darcyletter.isknownto(self))
                                       "\"I think not -- a more conceited, arrogant man I've never met.\" ";
                               else
                                       ;  // Thinks he's ok, don't object.
                       }
                       else if (d == Wickham) {
                               if (Wickham.betrothed)
                                       "\"But Mr. Wickham is already married!\" ";
                               else {
                                       if (darcyletter.isknownto(self))
                                               "\"What an idea!\" she replies in a scathing tone. ";
                                       else
                                               "\"What an idea!\" she smiles. ";
                               }
                       }
                       else if (d == Collins) {
                               "Elizabeth rolls her eyes slightly. ";
                       }
               }
       ;


       - saywillmarry(d): Performs the actual marriage.  Standard behavior is
       provided by an internal method "marry", so there's no need to call the
       inherited "saywillmarry."  The default version simply prints "<<self>>
       consents to marry <<d>>"; you'll probably want to override this with
       something more elaborate.

       If you order two NPCs to marry, the system calls "saywillmarry" such that
       "self" is the proposing actor and "d" is the proposee; e.g., the command
       "Collins, marry Charlotte", would call "Collins.saywillmarry(Charlotte)."

       If you propose (successfully) to an NPC, either by saying "marry Jane" or
       "Jane, marry me," it will call Jane.saywillmarry(Me).  This is admittedly
       backwards from the other case, but helps keep the code associated with an
       actor all in one place.


       WHO GETS CALLED WHEN:

       This gets a little complicated, so pay attention.  Marriage commands come
       in three possible forms:

               1. "marry X."
               2. "X, marry me."
               3. "X, marry Y."

       Cases 1 and 2 are handled identically, though they're different to the
       parser.  First it calls X.verMarry(Me) to give the proposee a chance to
       protest, then it calls Me.verMarry(X) to give the player the same chance.
       For Me.verMarry(X), it's actually more that the player might not be
       allowed to marry -- presumably they want to, or they wouldn't have
       proposed in the first place.  Finally, if no one complains, it calls
       X.saywillmarry(Me).

       In case 3, the player is ordering two NPCs to marry.  First the system
       calls X.verMarry(Y) -- X might not want to be pushed around by the player,
       in addition to not wanting to marry Y.  Then it calls Y.verMarry(X) to
       see if Y will accept X's proposal.

       To sum up:

               "marry Kitty" or "Kitty, marry me."
                       1. Kitty.verMarry(Me) -- does Kitty want to marry you?
                       2. Me.verMarry(Kitty) -- can you marry Kitty?

                       If no output results,
                       3. Kitty.saywillmarry(Me)

               "Wickham, marry Lydia."
                       1. Wickham.verMarry(Lydia) -- will Wickham propose to Lydia in the first place?
                       2. Lydia.verMarry(Wickham) -- will Lydia accept?

                       If no output results,
                       3. Wickham.saywillmarry(Lydia)
*/

#pragma C+

marryVersion: versionTag
   id="$Id: marry.t 1997/12/25 03:48:00 cdn Exp cdn $\n"
   author='Christopher Nebel'
   func='support for inter-character marriage'
;

marryVerb: deepverb
       verb = 'marry' 'wed'
       sdesc = "marry"
       doAction = 'Marry'
       validDoList(actor, prep, iobj) =
               (inherited.validDoList(actor, prep, iobj) + visibleList(Me))
       validDo(actor, obj, seqno) =
               (obj.isVisible(actor))
;

modify thing
       verMarry(obj) =
               "You have a very fertile imagination. "

       saybetrothed = {
               /* Necessary because adv.t doesn't have a sufficiently
                  detailed grasp of grammatical cases.  This could be
                  simplified under WorldClass. */
               if (self == Me)
                       "You are already spoken for. ";
               else
                       "\^<<self.thedesc>> is already spoken for. ";
       }
       verDoMarry(actor) = {
               /* Make sure they're not trying to marry themselves and
                  that both parties are unmarried. */
               if (actor == self)
                       "That would be awfully narcissistic. ";
               else if (self.betrothed)
                       self.saybetrothed;
               else if (actor.betrothed)
                       actor.saybetrothed;
       }
       doMarry(actor) = {
               /*
                       - "marry X" or "X, marry me."
                         X gets to object first, then Me, then call X.marry(Me).

                       - "X, marry Y."
                         X gets to object first, then Y, then call X.marry(Y).
               */
               local h, A, B;

               if (actor == Me)
                       A = self, B = actor;
               else
                       A = actor, B = self;

               h = outhide(true);
               A.verMarry(B);
               if (outhide(h)) {
                       /* Output resulted, which means there's an objection;
                          call it again and do no more. */
                       A.verMarry(B);
               }
               else {
                       h = outhide(true);
                       B.verMarry(A);
                       if (outhide(h)) {
                               /* Same drill as before. */
                               B.verMarry(A);
                       }
                       else
                               A.marry(B);
               }
       }
       marry(d) = {
               self.betrothed = d;
               d.betrothed = self;
               self.saywillmarry(d);
       }
       saywillmarry(d) = {
               /* Again, could be simplified under WorldClass. */
               if (d == Me)
                       "\^<<self.thedesc>> consents to marry you. ";
               else {
                       "\^<<self.thedesc>> consents to ";
                       if ((d.isHim and self.isHim) or (d.isHer and self.isHer))
                       "enter into a domestic partnership with ";
                       else "marry ";
                       "<<d.thedesc>>. ";
               }
       }
;

modify movableActor
       verMarry(d) = {
               "\^<<self.thedesc>> shows no interest in marriage. ";
       }
       actorAction(v, d, p, i) = {
               if (v == marryVerb)
                       /* Let 'em try... */;
               else
                       pass actorAction;
       }
;