!% -~S
!% $OMIT_UNUSED_ROUTINES=1
!% $MAX_STATIC_DATA=60000
!% $MAX_LABELS=5000
!% $MAX_ZCODE_SIZE=50000

Switches e;
! compile as z8 to use Inform stdlib instead of puny (for debugging)
#Iftrue (#version_number == 8);
Constant USEINFORM;
#Endif;
! Uncomment ONE of the two following lines, to show either time or score/turns
! Leaving both commented out makes the library bigger.
Constant STATUSLINE_TIME; Statusline time;
!Constant STATUSLINE_SCORE; Statusline score;
! 0 = Minimal error checking, 1 = Numeric error codes, 2: Error messages
Constant RUNTIME_ERRORS = 2;

Constant DEBUG; ! Provides debug verbs for both Inform and PunyInform
#IfnDef USEINFORM;
!Constant DEBUG_TIMER; ! requires special Ozmoo
!Constant DEBUG_PARSEANDPERFORM;
!Constant DEBUG_PARSEPATTERN;
!Constant DEBUG_PARSETOKEN;
!Constant DEBUG_CHECKNOUN;
!Constant DEBUG_GETNEXTNOUN;
!Constant DEBUG_SCOPE;
! Uncomment to add optional features to PunyInform
!Constant OPTIONAL_NO_DARKNESS;
Constant OPTIONAL_FLEXIBLE_INVENTORY;
Constant OPTIONAL_ALLOW_WRITTEN_NUMBERS;
Constant OPTIONAL_EXTENDED_METAVERBS;
Constant OPTIONAL_EXTENDED_VERBSET;
Constant OPTIONAL_PRINT_SCENERY_CONTENTS;
Constant OPTIONAL_SCORED;
Constant OPTIONAL_FULL_SCORE;
Constant OPTIONAL_GUESS_MISSING_NOUN;
Constant OPTIONAL_FULL_DIRECTIONS;
Constant OPTIONAL_SIMPLE_DOORS;
Constant OPTIONAL_SHIP_DIRECTIONS;
Constant OPTIONAL_MANUAL_SCOPE;
Constant OPTIONAL_MANUAL_REACTIVE;
Constant OPTIONAL_ORDERED_TIMERS;
#Iftrue (#version_number == 5);
Constant OPTIONAL_PROVIDE_UNDO;
#EndIf;

Constant INITIAL_LOCATION_VALUE = Library;

#EndIf;

!Constant TEST_BEFOREAFTER;

Constant Story      "Testbench";
Constant Headline   "^A sandbox for PunyInform development^";


! Serial "999999" enables timing in Ozmoo, meaning that the RTC (jiffies
! since start) can be read from $1c-->0
#IfDef DEBUG_TIMER;
Serial "999999";
#EndIf;


Constant MAX_CARRIED 3;
Constant SACK_OBJECT infiniteBag;

Constant AMUSING_PROVIDED;

#IfDef OPTIONAL_FULL_SCORE;
Constant MAX_SCORE = 7;
Constant OBJECT_SCORE = 1;
Array task_scores -> 3 0; ! inform doens't like array of length 1
Constant TASKS_PROVIDED;
Constant NUMBER_TASKS = 1;
#IfNot;
Constant MAX_SCORE = 3;
#EndIf;

[ Amusing;
	"^Did you try?^^1. Pushing John many times.^2. Going outside and
	looking at the sun";
];

! Setup cheap scenery
[SceneryReply w1 w2;
	Push:
		if(w1=='bird' && w2=='birds') "The birds fly away as you try.";
		"Now how would you do that?";
	default:
		rfalse;
];


! Setup flags
Constant FLAG_COUNT 8;
!Constant F_SQUIRREL_ESCAPED 0;
!Constant F_PARROT_FED 1;
!Constant F_BOUGHT_TICKETS 2;

#IfDef TEST_BEFOREAFTER;
[LookRoutine;
	"***** LookRoutine, yay!";
];
#EndIf;


#Ifdef USEINFORM;
Attribute reactive;
Include "Parser";
Include "VerbLib";
Include "../lib/ext_cheap_scenery.h";
Include "../lib/ext_flags.h";

[OnOff obj; ! TODO: is there something like this in I6? change name if needed
    if(obj has on) print "on";
    else print "off";
    return;
];
#IfNot;
Include "globals.h";
#EndIf;

[ DarkToDark;
	deadflag = 1;
	"You are eaten by a grue.";
];

[ PrintRank;
  print ", earning you the rank of ";
  if (score >= 3) "Treasure hunter.";
  if (score >= 2) "Explorer.";
  "Tourist.";
];

[ PrintTaskName task_number;
  switch (task_number) {
      0: "opening the box";
  }
];

[IsSpherical;
    print "*";
    return (noun == Orb);
];

Verb 'Frob'
    * noun = IsSpherical -> Frob
    * noun -> CantFrob;

[FrobSub;
    print "Frobbing ",(name) noun, " as requested.";
];

[ CantFrobSub;
    print "You can't frob that!";
];

Verb 'type'
	*	topic 'into'/'on' noun -> Type;

[ TypeSub;
    print consult_from;
];

Verb 'talk'
	*	'to'/'with'	creature	-> Talk;

[TalkSub;
	if (noun == player) "The conversation is hardly inspiring.";
	if (RunLife(noun,##Talk) ~= false) return;
	"You have nothing to say.";
];

#Ifdef USEINFORM;
#IfNot;
Constant EXT_MENU_STDLIB_MODE;
Include "ext_menu.h";
#Endif;

[ HelpItems;
    print "There is information provided on the following:^^";
    print "      Instructions^";
    print "      About this game^";
    print "      Game credits^";
];

[ HelpMenu;
    switch(menu_item) {
    0: item_width=10; item_name="Help and Information"; return 3;
    1: item_width=6; item_name="Instructions";
    2: item_width=7; item_name="About this game";
    3: item_width=6; item_name="Game credits";
    }
];

[ HelpInfo;
    switch(menu_item) {
    1: "You play this game like any old Infocom game.^^Common commands lnclude examine, open, close, save, restore and quit.";
    2: "This is a test game";
    3: "This was was created by us";
    }
];

[ HelpSub;
    DoMenu(HelpItems, HelpMenu, HelpInfo);
];
Verb 'help' * -> Help;

#Ifndef USEINFORM;
Include "ext_cheap_scenery.h";
Include "ext_flags.h";

! Setup custom library messages
Constant MSG_OPEN_YOU_CANT 1000;
Constant MSG_AREYOUSUREQUIT 1001;
Constant MSG_DISROBE_NOT_WEARING 1002;
Constant MSG_LOOK_BEFORE_ROOMNAME 1003;
[LibraryMessages p_msg p_arg_1 p_arg_2;
	switch(p_msg) {
	MSG_OPEN_YOU_CANT:
		"How would you go about opening ", (a) noun, "?";
	MSG_AREYOUSUREQUIT:
		print "You ain't no quitter eh? ";
		rtrue;
	MSG_DISROBE_NOT_WEARING:
		"Not worn!";
	MSG_LOOK_BEFORE_ROOMNAME:
		@new_line;
	}
	p_arg_1 = 0; p_arg_2 = 0; ! To avoid warnings
];

Include "puny.h";

Include "ext_waittime.h";

#Endif;

Constant SCN_WATER = "The water is so beautiful this time of year, all clear and glittering.";
[SCN_SUN;
	Examine:
	  deadflag = 1;
	  "As you stare right into the sun, you feel a burning sensation in your eyes.
	    After a while, all goes black. With no eyesight, you have little hope of
	    completing your investigations.";
	default:
		! Since this is a named routine, it will return true by default. Thus we
		! have to return false for all actions we don't care about
		rfalse;
];

Class Book
	with
		name 'books//p',
		description [;
			print "Self is ", self, "^";
			if(self provides hello) {
				print (The) self, " has a Hello value of ", self.hello, ". ^";
			}
			rfalse;
		],
		hello 0,
		party [a;
			print "Self is ", self, "^";
			print "a is ", a, "^";
!			print "b is ", b, "^";
!			print "c is ", c, "^";
		],
		before [;
			Open:
				"You've never been much of a reader.";
		],
	has openable;


Object CarPark "Car Park"
	with
		!short_name [; print "In front of the library!"; rtrue; ],
		initial "Wow, this is a huge car park!",
		description [;
			"You are in a car park in front of the library.
			You can enter to the north. There is a river off to the east.";],
		n_to Library,
		sw_to [; "There's an angry dragon blocking the path!"; ],
		e_to RiverBank,
		each_turn [;
		],
#IfDef TEST_BEFOREAFTER;
		after [; print "*** CarPark.after^"; ],
		before [; print "*** CarPark.before^"; ],
#EndIf;
		cant_go "Without a car the only viable exit is to the north.",
#IfDef OPTIONAL_FULL_SCORE;
	has scored light reactive;
#IfNot;
	has light reactive;
#EndIf;

Object -> Key "key"
	with
		description "It's a metal key, with a keyring attached to it.",
		name 'key' 'metal',
#IfDef TEST_BEFOREAFTER;
		after [; print "*** Key.after^"; ],
		before [; print "*** Key.before^"; ],
#EndIf;
#IfDef OPTIONAL_FULL_SCORE;
	has scored
#EndIf;
	has transparent;

Object -> -> Keyring "keyring"
	with
		name 'keyring',
		description "It's just a plain metal ring really.";

Object -> LeadWeight "lead weight"
	with
		name 'weight' 'lead',
		before [;
		Drop:
			remove self;
			move pencil to parent(player);
			PronounNotice(pencil);
			"When the lead weight hits the ground it sounds different.";
		]
	;

Object Pencil "pencil"
	with name 'pencil';

Object car "little red car" CarPark
	with name 'little' 'red' 'car',
		description "Large enough to sit inside. Among the controls is a
			prominent on/off switch. The numberplate is KAR 1.",
		describe [;
			@new_line;
			if(self has on) {
				"The red car sits here, its engine still running.";
			} else {
				"A little red car is parked here.";
			}
		],
		!when_on "The red car sits here, its engine still running.",
		!when_off "A little red car is parked here.",
		before [;
			Go: if (car has on) "Brmm! Brmm!";
				print "(The ignition is off at the moment.)^";
		],
	has  switchable enterable static container open;

Object RiverBank "River Bank"
  with
		description "The river is quite wide here. The sun reflects in the blue water, the birds are
      flying high up above.",
		w_to CarPark,
		nw_to Library,
		cheap_scenery
			'blue' 'water' SCN_WATER
			'bird' 'birds' "They seem so carefree."
			1 'sun' SCN_SUN,
	has light;

Object -> paper "paper"
	with
	 	name 'paper',
		describe "^There is a paper here.";

Object -> paperclip "paperclip"
	with
	 	name 'paperclip' 'clip',
		describe "^There is a paperclip here.";

Object Toilet "The toilet"
	with
		short_name [; print "A toilet"; rtrue; ],
		description[;
			print "This toilet is in dire need of cleaning. The toilet door leads west back into the library. ";
			if(Faucet has on) {
				"Water is streaming from the faucet into the sink. ";
			}
			"";
		],
#IfDef USEINFORM;
		w_to Library,
#IfNot;
		w_to ToiletDoor,
#EndIf;
	has ~light;

Object -> ToiletDoor "toilet door"
	with
		name 'toilet' 'door',
		describe [; rtrue; ],
!		parse_name [ _w;
!			_w = NextWord();
!			if(_w == 'toilet') {
!				_w = NextWord();
!				if(_w == 'door')
!					return 2;
!				return 0;
!			}
!			if(_w == 'door')
!				return 1;
!		],
		description "Surprisingly clean.",
		when_open "The toilet door stands slightly ajar.",
		when_closed "The toilet door is closed.",
#IfDef USEINFORM;
		door_to [;
			if(self in Toilet) return Library;
			return Toilet;
		],
		door_dir [;
			if(self in Toilet) return w_to;
			return e_to;
		],
#IfNot;
		door_dir (w_to) (e_to),
#EndIf;
		found_in Toilet Library,
		with_key [; if(second == Key or Sweater) return second;],
#IfDef TEST_BEFOREAFTER;
		after [; print "*** ToiletDoor.after^"; ],
		before [; print "*** ToiletDoor.before^"; ],
#EndIf;
	has door openable static lockable;

Object -> Goblin "Goblin"
	with
		short_name "Goblin",
		description "He is a mean looking creature, not to be messed around with. ",
		name 'goblin' 'creature',
		before[;
			Attack:
				print "But he has a grim looking scimitar, and you are defenceless. Are you sure that's a good idea? ";
				if(YesOrNo()) {
					deadflag = 1;
					"You'd never believe that so much blood was possible to be contained within this body. ";
				} else {
					"Wise call.";
				}

				rtrue;
		],
	has animate;


Object -> Sink "Sink"
	with
		short_name "sink",
		name 'sink' 'water',
		description[;
			print "It's a regular white sink with a faucet for turning water on or off. ";
			if(Faucet has on) {
				"There's water running into the sink from the faucet. ";
			}
			rtrue;
		],
	has container static scenery; !TODO: scenery objects still shown in room description

Object -> Faucet "faucet"
	with
		short_name "faucet",
		name 'faucet',
		description[;
			print "It's a faucet, possible to switch on/off. ";
			"It's currently ", (OnOff) self, ".";
		],
	has switchable static scenery; !TODO: scenery objects still shown in room description

Object Library "The Library"
	with
		name 'library', ! should create "you don't need to refer to ..."
		short_name [; print "Huge library"; rtrue; ],
		description "You are in an old and deserted library. There is an exit leading south and a toilet to the east.",
		s_to CarPark,
		se_to RiverBank,
#IfDef USEINFORM;
		e_to Toilet,
#IfNot;
		e_to ToiletDoor,
#EndIf;
#IfDef TEST_BEFOREAFTER;
		after [; print "*** Library.after^"; ],
		before [; print "*** Library.before^"; ],
#IfNot;
		before [;
			Look:
				if(self hasnt general) {
					give self general;
					!QuoteBox(quote_1);
				}
		],
#EndIf;
	has light;

Object -> Orb "Orb"
    with
        name 'orb';

Object -> LightSwitch "LightSwitch"
	with
		short_name "light switch",
		description "It's a switch located near the toilet. ",
		name 'light' 'switch',
		before [;
			Push:
				if(self has on)
					<<SwitchOff self>>;
				else
					<<SwitchOn self>>;
		],
		after[;
			SwitchOn:
				give toilet light;
#IfnDef USEINFORM;
				ship_directions_enabled = true;
				"You flip the light switch next to the toilet upwards. You get
					a weird feeling that ship directions now work. ";
#Endif;
			SwitchOff:
				give toilet ~light;
#IfnDef USEINFORM;
				ship_directions_enabled = false;
				"You flip the light switch next to the toilet downwards. You get
					a weird feeling that ship directions no longer work.";
#Endif;
		],
	has switchable static;


Object -> Sweater "sweater"
	with
		name 'sweater',
	with before [;
		Wear:
			if(PinkBook in player)
				"You don't put things on while holding the pink book. It might bring bad luck.";
			if(self has worn) rfalse;
			give self worn;
			"You put it on. The fit is perfect!";
	],
	has clothing;

Object -> Aquarium "aquarium"
	with
		name 'aquarium',
		article "an",
		description "A big glass aquarium with a lid.",
	has container open openable transparent static enterable;

Object -> LargeBox "The box" ! Set the object name to the name we want on the z3 status line when we're in the box. short_name will be used for all other cases.
	with
		short_name "box",
		name 'box',
		inside_description "The box looked pretty big from the outside, but you feel really confined in here.",
		description "It's a large box. You could probably fit several people in there.",
        time_out [;
            print "^The box goes BOOM.^";
        ],
		before [;
!			LetGo:
!				"No can do!";
		],
        after [;
			LetGo:
        		--score;
				"Yay, got rid of ", (the) noun, "!";
			Open:
#IfDef OPTIONAL_FULL_SCORE;
        	Achieved(0); ! opening the box is an achievement
#EndIf;
        Receive:
        	++score;
        	! win if you put two things in the box
        	if(score >= MAX_SCORE) deadflag = 2;
       	],
		time_left 0,
		timer_order 50,
	has container openable enterable static;

Book -> -> RedBook "red book"
		with
			name 'red' 'book',
			initial "On the bottom of the box, there is a red book.",
			description "The book is old and worn.",
			before [;
	 			Take:
	 				if(player in LargeBox) {
	 					"It's too hard to pick it up while inside the box.";
	 				}
			],
			after [;
	 			Take:
					if(BlueBook in player) {
						move BlueBook to location;
						"The blue book does not want to be near the red book and jumps out of your grasp and lands on the floor.";
					}
			];

Object -> -> Gun "gun"
	with
		name 'gun',
		before [;
			Examine: rfalse;
!			default: "It's very dangerous, better just leave it alone!";
		],
	has transparent;

Object -> -> -> Trigger "trigger"
	with
		name 'trigger';

Object -> Ladder "ladder"
	with
		name 'ladder',
		description [;
			print "It's very sturdy. ";
			<Search self>;
!			if(_PrintContents(" (which contains ", p_obj)) print ")";
		],
		after [;
			Receive: "Wow, you put ",(a) noun," on ", (the) self, "...";
		],
		inside_description "It's a really good ladder for standing on.",
	has supporter enterable;

Object -> -> Sticker "sticker"
	with
		name 'sticker',
		each_turn [;
#IfnDef USEINFORM;
!		if(location ~= thedark) "The sticker looks oh so sticky.";
#Endif;
		],
		add_to_scope Key,
	has reactive;

Object -> BlueBook "blue book" class Book
	with
		name 'book' 'blue',
		initial [;
			!print "Self is ", self, ". ";
			!if(BlueBook provides hello && BlueBook.hello > 10) {
			!	print "BlueBook.Hello is ", BlueBook.hello, ". ";
			!}
			print "A ";
			PrintShortName(self);
			" is standing on a shelf. Looking at ", (the) self, " makes you dizzy.";
		],
		before [;
			Turn:
				"You turn the book around and around. It is endlessly fun. ";
            Drop:
                if (self in player) {
                   StopDaemon(Self);
                }
		],
		after [;

			Take:
				if(RedBook in player) {
					print "The red book does not want to be near the blue book and jumps out of your grasp and lands on the floor.^^";
					move RedBook to location;
                }
!                StartDaemon(LargeBox);
                StartDaemon(self);
                StartTimer(LargeBox, 3);
				print "The blue box starts ticking...^";
				rtrue; ! stop normal printouts
		],
!		description "The book looks new and shiny.",
        daemon [;
            print "^The blue book glows annoyingly.^";
        ],
		bobby 7,
		hello 13,
		timer_order 190,
		pants (-1000) (-1001) (-1002) (-1003) (-1004) (-1005) (-1006) (-1007) (-1008) (-1009) (-1010) (-1011) (-1012) (-1013) (-1014) (-1015) (-1016) (-1017) (-1018) (-1019) (-1020) (-1021) (-1022) (-1023) (-1024) (-1025) (-1026) (-1027) (-1028) (-1029) (-1030) (-1031),
	has light; ! well, it is glowing

Object -> -> BlueBookTitle "title"
	with
		name 'title',
		description "The title of the blue book is ~War and peace~";


[ DebugLife rule;
	! same output as the table in
	! http://www.inform-fiction.org/manual/html/s18.html
    print "Rule ", (string) rule, ", action ";
    switch(action) {
	##Ask: print "Ask";
	##Answer: print "Answer";
	##Tell: print "Tell";
	##Take: print "Take";
	##Yes: print "Yes";
	##Give: print "Give";
	##NotUnderstood: print "NotUnderstood";
	default: print action;
	}
    print ", noun ";
    if (noun == 0) print "0";
   	else if(noun < top_object) print (the) noun;
   	else print "'",(address) noun,"'";
    print ", second ";
    if (second == 0) print "0";
    else if(second > 0 && second <= top_object) print (the) second;
    else print "'",(address) second,"'";
    if(consult_from > 0) print ", consult ", consult_from, " ", consult_words;
    print ", special_number ", special_number, "^";
];

Object -> John "John"
	with
		name 'john' 'johan',
		description "John is the meanest gangster around. Don't mess with him.",
		messed_with 0,
		add_to_scope Hair Beard,
		!add_to_scope [; PlaceInScope(Hair); ],
		react_before [;
			Take:
				if(noun == GreenBook && IndirectlyContains(parent(John), GreenBook) ) {
					print_ret (The) John, " says, ~Oh no! That's mine!~";
				}
		],
		before[;
			Pull, Push:
			switch(++self.messed_with) {
				1: "~Hey, don't push!~, John yells. ";
				2: "~I'm gonna make you regret that!~, John mutters. ";
				3: "~This is your final warning!~, John says. ";
				4: deadflag=1;
					"Oh dear, who would have guessed John was carrying a double barrelled gun with him to the library. ";

			}


		],
		orders [;
			Go: "~I must not leave this room.~";
			NotUnderstood:
				DebugLife("trying john.orders.NotUnderstood");
				rfalse;
				!print special_number, " ", special_word, " ", 'pink', " ", consult_from;
				!if(special_word == 0) "~You speak in riddles.~";
		],
		life [c w1 w2;
		! Attack Kiss WakeOther ThrowAt Give Show Ask Tell Answer Order
		Order:
			DebugLife("order");
			if(action==##Take && noun==LargeBox) "~", (The) noun, " is too big.~";
			if(action==##Take) "~I don't need more possessions.~";
			if(action==##NotUnderstood) "~I don't understand what you say.~";
			"John ignores your comment.";
		Show, Give:
			DebugLife("life");
			if(noun==PinkBook) <<Ask self PinkBook>>;
			"John doesn't appear to be interested.";
		Ask, Tell, Answer:
			DebugLife("life");
			w1=0;
			w2=0;
			wn=consult_from; c=consult_words; if(c>0) w1=NextWord();
			if(w1=='the') { c=c-1; if(c>0) w1=NextWord(); }
			if(c>1) w2=NextWord();
			if(c>2) "What you cannot say in few words, probably isn't worth saying.";

			if(w1=='help') "'I will help you, if you tell me how.'";
			if(second == 'pink') "~That is a nice pink book.~";
			"John ignores your question.";
		],
	has proper animate transparent reactive;

Object -> -> JohnsBall "pink ball"
  with  name 'pink' 'ball',
  has   concealed;

Object -> infiniteBag "infinite bag"
	with
		invent 0,
		name 'infinite' 'bag',
		article "an",
		description "An infinitely deep bag.",
	has container open openable light;

Book -> GreenBook "green book"
    with
!       name 'book' 'green',
        parse_name [ first_word;
            first_word = NextWord();
            if(first_word == 'green') {
                if(NextWord() == 'book') return 2;
                return 1;
            } else if (first_word == 'pinkish') {
                parser_action = ##PluralFound;
                if(NextWord() == 'green') {
                    if(NextWord() == 'book') return 3;
                    return 2;
                }
                return 1;
            } else if (first_word == 'book' or 'books') {
                return 1;
            }
            return 0;
        ],
        describe [; "^An old and torn green book is here."; ],
        description "The green book is nice and tidy.",
    ;

Book -> PinkBook "pink book"
    with
        invent "that stupid old pink book",
        parse_name [ first_word;
            first_word = NextWord();
            if(first_word == 'pink') {
                if(NextWord() == 'book') return 2;
                return 1;
            } else if (first_word == 'pinkish') {
                parser_action = ##PluralFound;
                if(NextWord() == 'book') return 2;
                return 1;
            } else if (first_word == 'book' or 'books') {
                return 1;
            }
            return 0;
        ],
        after [;
            Take:
                "You dared taking the pink book. Bravo!";
        ],
        description "The pink book is nice and tidy.",
    has clothing;

Object -> Torch "electric torch"
	with
		name 'torch' 'electric' 'flashlight',
		article "an",
		invent [;
			if(inventory_stage == 2) {
				print " (Ugly as sin)";
				rfalse;
			}
		],
!		description "A simple battery powered electric torch.",
		before [;
			Push:
				if(self has on)
					<<SwitchOff self>>;
				else
					<<SwitchOn self>>;
		],
		after[;
			SwitchOn:
				give self light;
#IfDef OPTIONAL_MANUAL_SCOPE;
				scope_modified = true;
#EndIf;
				"The torch is now providing light.";
			SwitchOff:
				give self ~light;
#IfDef OPTIONAL_MANUAL_SCOPE;
				scope_modified = true;
#EndIf;
				"The torch is dark again.";
		],
	has switchable;

! -- testing scope grammar token
! See: https://www.inform-fiction.org/manual/html/s32.html
[ QueryTopic;
  switch (scope_stage) {
      1: rfalse;
      2: ScopeWithin(questions); rtrue;
      3: "At the moment, even the simplest questions confuse you.";
  }
];

[ QuerySub; noun.description();];

Verb 'what' * 'is'/'was' scope=QueryTopic -> Query;

Class MultiTopic
	with
		number 0,
		parse_name [ _w1 _w2 _i _sw1 _sw2 _len;
			_w1 = NextWordStopped();
			_w2 = NextWordStopped();
			_i = 0;
			_len = self.#topics / 2;
#IfTrue RUNTIME_ERRORS > RTE_MINIMUM;
#IfTrue RUNTIME_ERRORS == RTE_VERBOSE;
			if(_len % 3 > 0)
				"ERROR: multi_topic topics property of ", (the) self," has incorrect # of values!^";
#IfNot;
			if(_len % 3 > 0)
				"ERROR: multi_topic #1!^";
#EndIf;
			while(_i < _len) {
				_sw1 = self.&topics-->(_i+2);
#IfTrue RUNTIME_ERRORS == RTE_VERBOSE;
				if(~~(_sw1 ofclass String or Routine))
					"ERROR: Element ", _i+2, " in multi_topic topics property of ", (the) self," is not a string or routine!^",
						"Element: ", (name) _sw1, "^";
#IfNot;
				if(~~(_sw1 ofclass String or Routine))
					"ERROR: multi_topic #2!^";
#EndIf;

				_i = _i + 3;
			}
			_i = 0;
#endif;
			while(_i < _len) {
				_sw1 = self.&topics-->_i;
				_sw2 = self.&topics-->(_i+1);
				if(_w1 == _sw1 && _w2 == _sw2) {
					self.number = _i;
					return 2;
				}
				if(_w1 == _sw1 or _sw2) {
					self.number = _i;
					return 1;
				}
				_i = _i + 3;
			}
			! It would make sense to return 0 here, but property
			! routines return 0 by default anyway.
		],
		description [ _k;
			_k = self.&topics-->(self.number + 2);
			if(_k ofclass Routine) {
				_k();
				rtrue;
			}
			print_ret (string) _k;
		];

Object questions "questions";

MultiTopic -> MultiQuestion_1 "MultiQuestion_1"
	with
		topics
			'long' 'count' "The Long Count is the Great Mayan cycle of
			time, which began in 3114 BC and will finish with
			the world's end in 2012 AD."
			1 'samarkand' "Samarkand is a nice city in south-eastern Uzbekistan.";

! Object -> "long count"
  ! with name 'long' 'count',
       ! description "The Long Count is the great Mayan cycle of
           ! time, which began in 3114 BC and will finish with
           ! the world's end in 2012 AD.";
! Object -> "samarkand"
  ! with name 'samarkand',
       ! description "Samarkand is a city in south-eastern Uzbekistan.";

Object  Hair "John's hair"
  with  name 'hair' 'head',
        before [;
            Examine,Pull,Search:
                "John doesn't want to you to mess up his hair.";
            default: "Huh?";
        ],
  has   proper concealed;
Object  Beard "John's beard"
  with  name 'beard',
        before [;
            Examine,Pull,Search:
                "You wouldn't dare to touch his beard.";
            default: "Huh?";
        ],
  has   proper concealed;

!Array quote_1 --> 5 35
!"When I die, I want to go peacefully"
!"in my sleep like my grandfather."
!"Not screaming in terror, like the"
!"passengers in his car."
!"               -- Jack Handey";

[Initialise;
#IfnDef USEINFORM;
	ship_directions_enabled = false;
#Endif;
	give player reactive;
	thedark.short_name = "Yucky darkness";
	thedark.initial = "Oh, so scary.";
	thedark.description = "Now you are really scared.";
	SetTime(9*60+30, 5); ! 9:30 am, each turn 5 minutes
#Ifdef USEINFORM;
	location = Library;
#Endif;
!	PlayerTo(LargeBox);
!	print "Parrot fed = ", FlagIsSet(F_PARROT_FED),"^";
!	SetFlag(F_PARROT_FED);
!	print "Have set. Parrot fed = ", FlagIsSet(F_PARROT_FED),"^";
!	ClearFlag(F_PARROT_FED);
!	print "Have cleared. Parrot fed = ", FlagIsSet(F_PARROT_FED),"^";
!	print "Parrot fed != ", FlagIsClear(F_PARROT_FED),"^";

!	b = BlueBook.&pants;
!	for(i=0 : i<BlueBook.#pants / 2 : i++)
!		print "BlueBook pants(",i,") is ", b-->i, "^";
!	BlueBook.description();
!	print "BlueBook.party is ", BlueBook.party, "^";
!	BlueBook.party(5);
	move Gun to John;
	move BlueBook to CarPark;
	move PinkBook to CarPark;
	move GreenBook to CarPark;

	print "^^Welcome to the Testbench... This is where all sorts of PunyInform
		features are tried out. Don't expect much of a game here. Currently, you
		can win by putting stuff in the box, or lose by checking out the sun by
		the river or by pushing John around.";
];
#Ifdef USEINFORM;
Include "Grammar";
#Endif;

[NotHeld;
	if(noun in player) rfalse;
];

Extend 'remove' first
	* noun=NotHeld -> Take;
