!% -~S
!% -e
!% $OMIT_UNUSED_ROUTINES=1
!% $ZCODE_LESS_DICT_DATA=1

! Testbench - a platform for trying things out in PunyInform

! 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.
Statusline time;
!Constant STATUSLINE_SCORE; Statusline score;

Constant RUNTIME_ERRORS = 0; ! 0 = Minimal error checking, 1 = Numeric error codes, 2: Error messages

#IfnDef USEINFORM;
! Uncomment to add optional features to PunyInform
!Constant OPTIONAL_NO_DARKNESS;
Constant STATUSLINE_TIME;
Constant OPTIONAL_ALLOW_WRITTEN_NUMBERS;
Constant OPTIONAL_FLEXIBLE_INVENTORY;
Constant OPTIONAL_EXTENDED_METAVERBS;
Constant OPTIONAL_EXTENDED_VERBSET;
!Constant OPTIONAL_LANGUAGE_NUMBER;
!Constant OPTIONAL_LIST_TOGETHER;
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_REACTIVE_PARSE_NAME;
Constant OPTIONAL_ORDERED_TIMERS;
Constant MAX_SCOPE = 50;

#Iftrue (#version_number == 5);
Constant OPTIONAL_PROVIDE_UNDO;
#Endif;

Constant INITIAL_LOCATION_VALUE = Library;

!Constant DEBUG; ! Provides debug verbs for both Inform and PunyInform
!Constant DEBUG_PARSEANDPERFORM;
!Constant DEBUG_PARSEPATTERN;
!Constant DEBUG_PARSETOKEN;
!Constant DEBUG_PARSENOUNPHRASE;
!Constant DEBUG_GETNEXTNOUN;
!Constant DEBUG_SCOPE;
!Constant DEBUG_TIMER; ! requires special Ozmoo
#EndIf;

!Constant TEST_BEFOREAFTER;

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

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

Constant DEATH_MENTION_UNDO;
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;
Include "Parser";

Attribute reactive;

Include "VerbLib";
Include "ext_cheap_scenery.h";
Include "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;

[ChooseObjects obj code;
	if(code == 2) {
		if(action_to_be == ##SwitchOn or ##SwitchOff) {
			if(obj has switchable)
				return 2;
		}
		return 0;
	}
!	print " ALL:", (name) obj;
	rfalse; ! Don't interfere with ALL
];

[InScope p_actor;
	if(p_actor == player && location == thedark && John in real_location)
		PlaceInScope(John);
	rfalse;
];

[ParseNoun obj w;
	if(obj == LargeBox) {
		w = NextWord();
		if(w == 'crate') { wn = wn + 7; return 1; } ! Change wn just to check that nothing breaks
		if(w == 'huge') return -1;
		wn--;
	}
	if(obj == PinkBook) {
		w = NextWord();
		if(w == 'tiny') return -1;
		wn--;
	}
	return -1;
];

[ 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";
  }
];


#Ifndef USEINFORM;
Include "ext_cheap_scenery.h";
Include "ext_flags.h";
!Include "ext_talk_menu.h";
Constant EXT_MENU_STDLIB_MODE;
Include "ext_menu.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:
		if(turns >= 0) new_line;
	}
	p_arg_1 = 0; p_arg_2 = 0; ! To avoid warnings
];



[DisallowTakeAnimate;
	if(noun == Lizard) rfalse;
];



Include "puny.h";

Include "ext_waittime.h";

#Endif;

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

[ HelpMenu;
! Note that we set item_width to half the length of each name, since we have
! defined EXT_MENU_STDLIB_MODE to make ext_menu fully compatible with the
! standard library DoMenu implementation.
    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;



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.";
];

[ScanScope;
	switch(scope_stage) {
		1: rfalse;
		2: ScopeWithin(RiverBank); rtrue;
		3: "You're crazy!";
	}
];

[BangParse;
#Ifdef USEINFORM;
	return ParseToken(ELEMENTARY_TT, NUMBER_TOKEN);
#Ifnot;
	return ParseToken(TT_OBJECT, NUMBER_OBJECT);
#Endif;
];

Verb 'bang'
	* BangParse -> Bang;

[BangSub;
	"Noun is ", parsed_number, ".";
];

[WowScope;
	switch(scope_stage) {
		1: rfalse;
		2: ScopeWithin(CarPark); rtrue;
		3: "You can't wow that, you crazy person!";
	}
];

[WowParse x;
#Ifdef USEINFORM;
	x = ParseToken(SCOPE_TT, WowScope);
#Ifnot;
	x = ParseToken(TT_SCOPE, WowScope);
#Endif;
	print "ParseToken returned ", x, "^";
	return x;
];

Verb 'wow'
	* WowParse -> Wow;

[WowSub;
	print (The) noun, " is in scope!^";

!	LoopOverScope(WowLoop);
];

Verb 'scan'
	* scope=ScanScope -> Scan
	* 'local' noun -> Scan;

[ScanSub;
	if(AfterRoutines()) rtrue;
	print "Scanning ", (the) noun, "... Clear!^";
];

Verb 'telly'
	* number 'to' topic -> Telly;

[ TellySub;
	"You telly ", noun, " to ", (address) second, ".";
];

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.
			Alpha Repair can be seen to the south.
			A table stands inexplicably in the middle of the car park.
			A weird smell permeats the air.";],
		n_to Library,
		sw_to [; "There's an angry dragon blocking the path!"; ],
		e_to RiverBank,
		s_to AlphaRepair,
		each_turn [;
			print (string) random(
					"^An owl is howling nearby.",
					"^A terrible scream is heard from the library.",
					"^The sound of many birds comes from the east.");
			"";
		],
		scenery_outdoors
			CS_NO_ADJ 'air' "The air is fresh here!"
            'soil' 'dirt' "The ground is damp after last night's rain.",
		cheap_scenery
			CS_NO_ADJ 'library' [;
				Examine: "It's a very nice library.";
				Burn: "Oh, better not!";
			]
			CS_PARSE_NAME [ i;
			    while(NextWord() == 'fabulous' or 'huge' or 'car' or 'park') i++; return i;]
			  "A fabulous, huge car park!"
			'weird' 'smell' "A bit like a skunk really."
            'soil' 'dirt' "The ground is surprisingly dry here!"
			CS_ADD_LIST CarPark (scenery_outdoors),
#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 -> Blue  "blue table"
	with
		name 'blue' 'table',
		has supporter scenery;

Object -> Table "table"
	with
		name 'table',
		has supporter scenery;

Object -> -> "obvious plate"
	with
		name 'obvious' 'plate',
		article "an";

Object -> -> "concealed plate"
	with
		name 'concealed' 'plate'
	has concealed;


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;
	has transparent
#IfDef OPTIONAL_FULL_SCORE;
	scored
#EndIf;
;

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);
#IfDef OPTIONAL_MANUAL_SCOPE;
			scope_modified = true;
#EndIf;
			"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.",
	  	d_to SecretHatch,
		w_to CarPark,
		nw_to Library,
		cheap_scenery
			'blue' 'water' SCN_WATER
			'bird' 'birds' "They seem so carefree."
			CS_NO_ADJ 'sun' SCN_SUN
			CS_ADD_LIST CarPark (scenery_outdoors),
	has light;

Object -> ExoSkeleton "exoskeleton"
	with
		description "A powered exoskeleton, designed to give its user superhuman
			strength. You can enter the exoskeleton.",
		name 'exo' 'exoskeleton' 'skeleton',
	has enterable transparent;

Object -> Microphone "microphone"
	with
		name 'microphone',
		orders [;
			"~Don't you boss me around!~ says the microphone.";
		],
		before [;
			Ask:
				print "Before: Microphone - Ask^";
			AskFor:
				print "Before: Microphone - AskFor^";
			Answer:
				print "Before: Microphone - Answer^";
			Tell:
				print "Before: Microphone - Tell^";
		],

		life [;
			Ask:
				"Life: Microphone - Ask";
			AskFor:
				"Life: Microphone - AskFor";
			Answer:
				"Life: Microphone - Answer";
			Tell:
				"Life: Microphone - Tell";
		],
	has talkable;

Object -> SecretHatch "secret hatch"
	with
		name 'secret' 'hatch',
		description "It's very secret.",
		after [;
			give self ~concealed;
		],
		door_to Library,
		door_dir (d_to),
	has static concealed door openable;

Object -> Lizard "cute little lizard"
	with
		name 'cute' 'little' 'lizard',
		description "It looks at you with big eyes.",
		after [;
			Take:
				"It dives into your breast pocket, spins around
					and peaks out, ready for adventure!";
			Drop:
				deadflag = 1;
				"He jumps up and bites your hand. You die.";
		],
	has animate neuter;

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 AlphaRepair "Alpha Repair"
	with
		description "You're in alpha repair. The car park lies to the north,
			while beta repair lies to the south. The alpha repair panel and
			the conveyor belt are the most obvious features here.",
		n_to CarPark,
		s_to BetaRepair,
		repair_name 'alpha',
		cheap_scenery
			CS_PARSE_NAME
				[ i;
					while(NextWord()==self.repair_name or 'repair' or 'panel')
						i++;
					return i;
				]
				[; Examine: "The ", (address) self.repair_name, " repair panel looks ok."; ]
			'conveyor' 'belt' "It doesn't look broken.",
	has light;

Object BetaRepair "Beta Repair"
	with
		description "You're in beta repair. Alpha repair lies to the north.
			The beta repair panel and the conveyor
			belt are the most obvious features here.",
		n_to AlphaRepair,
		repair_name 'beta',
		cheap_scenery CS_ADD_LIST AlphaRepair (cheap_scenery),
	has light;

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, and there's a dark doorway
				to the east. ";
			if(Faucet has on) {
				"Water is streaming from the faucet into the sink. ";
			}
			"";
		],
#IfDef USEINFORM;
		w_to Library,
#IfNot;
		w_to ToiletDoor,
#EndIf;
		e_to DarkChamber,
	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 could 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.";
			"";
		],
	has container static scenery;

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;

Object DarkChamber "Dark Chamber"
	with
		description "It's almost entirely dark.",
		w_to Toilet;

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 -> Dial "dial"
	with
		name 'black' 'dial',
		initial "There's a black dial on the wall.",
		description [;
			print "It does from 0 to 99. It's currently set to ",
					self.time_left, ".^";
		],
		time_left 0,
		before [;
			Turn, Push, Pull: "Try SET DIAL TO 3.";
			SetTo:
				if(second >= 0 && second < 100) {
					if(HDR_GAMEFLAGS-->0 & 128) {
						! sound effect: 3, start, full volume, once
						print "[Can you hear the sound effect?]^^";
						@sound_effect second 2 255 0;
					}
					self.time_left = second;
					"Ok.";
				}
				"It only goes from 0 to 99.";
		],
	has static;


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' 'red',
	with before [;
		Wear:
			if(PinkBook in player)
				"You don't put things on while holding the pink book. It might bring bad luck.";
	],
	after [;
		Wear:
			"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 ->-> Apple "apple"
	with name 'apple',
   	has concealed;

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 'large' '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 [;
	 			Transfer:
					"NO WAY!";
	 			Take, Remove:
	 				if(player in LargeBox) {
	 					"It's too hard to pick it up while inside the box.";
	 				}
					if(BlueBook in player) {
						print "The blue book does not want to be near the red book and jumps out of your grasp and lands on the floor.^^";
						move BlueBook to location;
					}
			];

Book -> -> BrownBook "brown book"
		with
			name 'brown' 'book',
			description "The book is beautiful..";

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 -> Shelf "shelf"
	with
		name 'shelf',
		capacity 1,
	has supporter enterable;

Object -> -> Boots "boots"
	with
		name 'pair' 'of' 'boots',
		article "a pair of",
	has supporter enterable pluralname;

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',
		time_left 0,
		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. ";
		],
		after [;
!            Drop:
!               StopDaemon(Self);
			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);
				self.time_left = turns; ! Remember which turn we started
                StartTimer(LargeBox, 3);
				print "The blue book starts ticking...^";
				rtrue; ! stop normal printouts
		],
!		description "The book looks new and shiny.",
        daemon [;
			if(self.time_left < turns - 1) {
				if(TestScope(self))
	            	print "^The blue book ticks loudly.^";
				else
	        		print "^You hear a faint ticking in the distance.^";
			}
        ],
		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~";

#Ifdef DEBUG;
[ DebugLife rule;
	! same output as the table in
	! http://www.inform-fiction.org/manual/html/s18.html
    print "Rule ", (string) rule, ", action ", (DebugAction) action, ", 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, "^";
];
#Endif;

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); PlaceInScope(Beard); ],
		react_before [;
			Take:
				if(noun == GreenBook && IndirectlyContains(parent(John), GreenBook) ) {
					print_ret (The) John, " says, ~Oh no! That's mine!~";
				}
		],
		react_after [;
			Scan:
				if(noun == Paper)
					"John scoffs at your feeble attempt to scan the paper.";
		],
		before[;
			Pull, Push:
			switch(++self.messed_with) {
				1: "~I'm gonna make you regret that!~, John mutters. ";
				2: "~This is your final warning!~, John says. ";
				3: 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:
#IfDef DEBUG;
				DebugLife("trying john.orders.NotUnderstood");
#EndIf;
				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:
#IfDef DEBUG;
			DebugLife("order");
#EndIf;
			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:
#IfDef DEBUG;
			DebugLife("life");
#EndIf;
			if(noun==PinkBook) <<Ask self PinkBook>>;
			"John doesn't appear to be interested.";
		Ask, Tell, Answer:
#IfDef DEBUG;
			DebugLife("life");
#EndIf;
			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 reactive;

Object -> -> JohnsBall "pink ball"
  with  name 'pink' 'ball';

Object -> infiniteBag "infinite bag"
	with
		invent 0,
		name 'infinite' 'bag',
		article "an",
	has container open openable;

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.",
	has reactive;


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 reactive;

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;
		],
	has reactive;

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 John;
!	move Sweater to John;
!	give Sweater worn;
#Ifndef USEINFORM;
	scope_modified = true;
#Endif;
	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;

Constant MAX_NUMS = 5;
Array entered --> MAX_NUMS + 1;

[ EnterNumGPR num count;
	while(true) {
		num = TryNumber(wn);
		if(wn <= num_words && num >= 0) {
			wn++;
			count++;
			if(count <= MAX_NUMS)
				entered-->count = num;
		} else {
			if(count == 0) return GPR_FAIL;
			entered-->0 = count;
			return GPR_PREPOSITION;
		}
	}
];

[EnterNumbersSub i;
	i = 0;
#Ifdef DEBUG;
	for(i = 1 : i <= entered-->0 : i++)
		print "ENTERED NUM: ", entered-->i, "^";
#Endif;
	"That's hardly useful.";
];

[EnterTopicSub;
	"You need to enter one or more numbers, like ENTER 123 456.";
];


Extend 'enter'
	* EnterNumGPR -> EnterNumbers
	* topic -> EnterTopic;
