! phtalkoo.h: the Photopia-based menu conversation object-oriented library
!
! based on phototalk.inf by Adam Cadre, the Photopia dialogue source
! object-oriented and somewhat changed into phtalkoo.h by David Glasser 21-Jul-1999
! improved for Triform 1.2 by Hunter Hoke 24-Jan-18

! This file is a library based on the Photopia menu conversation
! system.  Its principles differ little from Adam's binary-flag module, flags.h.
!
! Each NPC needs to inherit from the converser class.  They must each
! provide the member functions SayQ(x) and Respond(x), which print
! the xth quip's 'title' and response; Respond often does stuff in
! addition to printing the response.
!
! Each NPC should also provide an InitQuips() member function, which
! should turn on (see below) the quips that should default to being
! on.  You should put the following in your Initialise function:
!     QuipInit();
!
! NPC.SetQuip(42, 1) turns the NPC's quip #42 on; NPC.SetQuip(42, 0)
! turns it off.  NPC.QuipOn(42) and NPC.QuipOff(42) are shortcuts for
! the above.  NPC.QuipsOff(N, a, b, ...) turns off N quips, which are the
! rest of the arguments; N must be less than 7, as Inform only accepts 7
! arguments per function.  There is also NPC.QuipsOn, which does what you'd
! expect.  Finally, NPC.TestQuip(42) returns 1 if quip 42 is on or 0 if not.
!
! Quips are 0-based; by default, you can use fifty-six quips, from 0
! to 55.
!
! NOTE TO PHOTOTALK.INF USERS: In Adam's file, QuipOn was the tester
! and there was no shortcut for setting.  Here, TestQuip tests and
! QuipOn sets.
!
! You may wish to add life and/or orders routines to the converser
! class, and/or modify the various messages below.
!
! If any given converser needs more than 56 quips, you can change
! (either in the class below or by overloading on the specific converser)
! the qflag declaration to have N/8 zeros, and MAXQUIP to be N-1, where
! N is the amount of quips you want for the converser, and is a multiple of
! 8.
!
! You may want to Replace some or all of AskSub, AskForSub, TellSub,
! GiveSub, ShowSub, and AnswerSub with:
!      [ FooSub; "Please use >TALK TO PLAYER to interact in this game."; ];
! and put the following into the converser class:
!      orders [; "Please use >TALK TO PLAYER to interact in this game."; ]
!
! An example can be found, commented out, at the end of this file.

[ quipInit o;
     objectloop (o ofclass converser) { o.InitQuips(); }
];

Class Converser
class Person
   with
   	qflag 0 0 0 0 0 0 0, ! 56 should be enough
	MAXQUIP 55,  ! like a constant
	before [x z ok selected;
		TalkTo:
		for (x=0 : x <= self.MAXQUIP : x++) {
			if (self.TestQuip(x)) { ok++; }
		}
		
		if (ok > 0) {
     print "What ";
     CSubjectVoice(player, "do", "do", "does", "did");
     print " ";
     CSubjectVerb(player, false, true, "want", "want", "want", "want"); 
     print " to say?^^";
			
			! List the lines you have to choose from.
			
			for (x=0: x <= self.MAXQUIP: x++) {
				if (self.TestQuip(x)) {
					z++;
					print "[", z, "] ";
					self.SayQ(x);
				}
     		}

			! Get the choice and respond to it.
     
			new_line;
			do {
				print "Select an option or 0 to say nothing >> ";
@read buffer parse DrawStatusLine;
				selected = TryNumber(1);
			} until ((selected >= 0) && (selected <= z));
 			if (selected == 0)
{ 				print "^"; CSubjectDont(player,true); print " say anything after all.^"; }
			if (selected ~= 0) {
				ok = 0; new_line;
				for (x=0: x <= self.MAXQUIP: x++) {
					if (self.TestQuip(x)) {
						ok++;
						if (ok == selected) {
							self.Respond(x);
						} ! end if it's the right one
					} !end if it's even on
				} !end for all the possible quips
			} !end if we want any response
			
			rtrue;
		} !end if there are any quips
		
		print_ret     CSubjectCant(actor,true), " think of anything in particular to say.";
	],
	SetQuip [ line onoff     y z;
		if (line > self.MAXQUIP) {
			"Oh, dear!  Too high a quip in ", (the) self, ".SetQuip! [BUG]";
		}
		y = line / 8;  ! y is the bytecount
		z = line % 8;  ! z is the bitnum
		z = TwoPower(z);  ! now a byte with only that bit set
		if (onoff == 1) {
			self.&qflag->y = self.&qflag->y | z; }
		if (onoff == 0) {
			self.&qflag->y = self.&qflag->y & ~z; }
		],
	QuipOn [line; self.SetQuip(line, 1); ],
	QuipOff [line; self.SetQuip(line, 0); ],
	QuipsOn [n a b c d e f; ! this function can probably be written better
		if ((n < 0) || (n > 6)) { ! 0 is ignored
			"QuipsOn called with first arg ", n, "!";
		}
		if (n == 0) return; self.QuipOn(a);
		if (n == 1) return; self.QuipOn(b);
		if (n == 2) return; self.QuipOn(c);
		if (n == 3) return; self.QuipOn(d);
		if (n == 4) return; self.QuipOn(e);
		if (n == 5) return; self.QuipOn(f);
		],
	QuipsOff [n a b c d e f; ! this function can probably be written better
		if ((n < 0) || (n > 6)) { ! 0 is ignored
			"QuipsOff called with first arg ", n, "!";
		}
		if (n == 0) return; self.QuipOff(a);
		if (n == 1) return; self.QuipOff(b);
		if (n == 2) return; self.QuipOff(c);
		if (n == 3) return; self.QuipOff(d);
		if (n == 4) return; self.QuipOff(e);
		if (n == 5) return; self.QuipOff(f);
		],
	TestQuip [line      y z;
		if (line > self.MAXQUIP) {
			"Oh, dear!  Too high a quip in ", (the) self, ".TestQuip! [BUG]";
		}
		y = line / 8;
		z = line % 8;
		z = TwoPower(z);
		if (self.&qflag->y & z == z) { rtrue; }
		rfalse;
	],
	SayQ [; "Something? [BUG]";],
	Respond [; "I don't know what to say. [BUG]"; ],
	InitQuips [; "~My InitQuips should be overloaded,~ says ", (the) self, ". [BUG]";],

	has animate
;

[ TalkToSub;  ! One method of communication to rule them all
   if (noun == player) "Talking to yourself is not particularly fun.";
   if (~~(noun ofclass converser)) "Generally, it's best to talk to living things.";
                  print (The) noun;
                  if (noun has pluralname) print " don't";
                          else print " doesn't";
                              " seem interested.";
];

[ TwoPower a;
	switch(a) {
	0:	return $$000000001;
	1:	return $$000000010;
	2:	return $$000000100;
	3:	return $$000001000;
	4:	return $$000010000;
	5:	return $$000100000;
	6:	return $$001000000;
	7:	return $$010000000;
	}
	"Error: TwoPower out of range: ", a, "!"; 
];