!----------------------------------------------------------------
! Moderator.h, by Emily Short
! May be incorporated in Inform code freely with or without credit
! and modified according to the requirements of the author.
! Contact me at
[email protected] if you encounter any bugs.
!
! Moderator.h is primarily useful for games with a fairly complex plot
! where the author wants to be able to block certain actions and movements
! at some times but not at others. Moderator does this through the
! omnipresent "moderator" object (which supervises everything the player
! does) and a series of scene objects. Only one scene can be active
! at a time.
!
! Scenes have the following characteristics:
!
! 1. A scene can have its own set of react_befores, so that the PC is kept
! from doing certain activities during that portion of the game.
! Useful if a lot of the game is going to take place in the same rooms,
! so that these properties can't be tied to location etc.
!
! 2. A scene can determine whether a player is allowed to move into a
! certain location.
!
! 3. A scene's "trigger" property is called every turn, allowing it to do a
! check to determine whether it is time to move on to a new scene. It
! is also possible to have the trigger property print something.
!
! 4. A scene ends when its endflag is set to a non-zero value, and a
! new scene is chosen. Scenes that have already occurred receive the
! general attribute.
!
!----------------------------------------------------------------
!
! INSTRUCTIONS:
!
! Include Moderator.h after verblibm.h.
!
! Before including the verblibm, put in
! Replace GoSub;
!
! Create a scene object for each discrete segment of the game for which
! you want to be able to exert special control.
!
! During Initialise, do
! Moderator.current = <FirstSceneOfGame>;
!
!----------------------------------------------------------------
!
! SCENE PROPERTIES:
!
! locator: a routine which receives the room into which a player is moving as
! an argument during any Go action. It may print or not print as
! it chooses, but should return false to permit the go action to proceed as
! normally, true to prevent go action. (If it prevents, it should print
! to explain the failure, just as when preventing an action in before.)
! The convenient thing is that this routine works regardless of how the
! player is entering the new location and what the starting location is.
! The presence of doors is also irrelevant.
!
! NB: locator can be used to implement an NPC that follows the player;
! have locator move the NPC into the new room and print something like
! "White accompanies you as you walk."
!
! WARNING:
! One weird caveat: if you want to prevent the player moving into a
! location that is dark, you will have to specify the dark room as well
! as the actual room that's there. Personally, these days I rarely write
! a game that doesn't have light in all the rooms automatically.
!
! react_before: as always.
!
! trigger: like an each_turn property for the scene.
!
! number: incremented each turn during the scene. Can be used to make a scene
! last for a set number of turns, or to schedule events at a given
! time within the scene.
!
! endflag: indicates whether the scene is over. If it is positive, Moderator
! will call the scene's advance property during this turn. One can,
! for instance, set the endflag to different values depending on the
! outcome of the scene.
!
! advance: describe the end of the scene (optionally) and set the new scene, by
! doing Moderator.current = <name of new scene>.
!
! startup: does anything required to set up the scene as it is beginning.
! This routine could, for instance, move the player into a location,
! bring an NPC on stage, etc.
!
!
!
!----------------------------------------------------------------
Object Moderator,
with
current 0, ! Set to whatever the current scene is
found_in [;
rtrue;
],
react_before [ x;
default:
if (self.current && self.current provides react_before)
{ x = self.current.react_before();
return x;
}
rfalse;
],
locator [ newroom i x;
if (self.current provides locator)
{ i = self.current.locator(newroom);
if (i) rtrue;
}
rfalse;
],
each_turn [ ;
if (self.current == 0) rfalse;
self.current.number++;
if (self.current provides trigger && self.current.endflag == 0)
self.current.trigger();
if ((self.current == 0) || (~~(self.current provides advance))
|| (~~(self.current provides endflag))) rfalse;
self.advance();
],
advance [;
if (self.current.endflag == 0) rfalse;
give self.current general;
self.current.advance();
if (self.current hasnt general)
self.current.startup();
],
has scenery concealed;
Class Scene,
with
endflag 0,
number 0,
startup [;
rfalse;
],
trigger [;
rfalse;
],
advance [;
rfalse;
];
! EXAMPLE CODE:
! Scene FirstScene,
! with
! react_before [;
! xyzzy: "You're too busy getting ready for Paris to mess around
! with magic spells.";
! listen: "Sounds like someone is banging around upstairs.";
! ],
! locator [ newroom;
! if (newroom == Attic)
! { "You don't dare enter the attic while your spouse is looking
! for that map of Paris.";
!
! ! (Because this returns true, the player will never enter
! ! the attic during this scene.)
! }
!
! ],
! trigger [;
! if (self.number > 5) ! true after 6 turns have elapsed
! { self.endflag = 1; ! this is the end of this scene
!
! "~Honey,~ calls a familiar voice from the attic. ~Could you give
! me some help up here?~";
! }
! ],
! advance [;
! Moderator.current = HelpingSpouse; ! set up the new scene
! "^There's a loud thumping from upstairs. Sighing, you climb up
! through trap door...";
! ];
!
! Scene HelpingSpouse,
! with
! startup [;
! move spouse to Attic;
! PlayerTo(Attic); ! This begins the new scene
! ],
! locator [ newroom;
! if (newroom == Downstairs)
! {
! self.endflag = 1;
! print "~Never mind,~ you say. ~I'm no good at this.
! You'll have to find it on your own.~";
! rfalse;
!
! ! (Returns false, so the player is allowed to go, but
! ! the scene will end at this point.)
! }
!
! ],
! trigger [;
! if (MapOfParis in player)
! { self.endflag = 2;
! }
! ],
! advance [;
! if (self.endflag == 1)
! { Moderator.current = PackingAllAlone;
! ...
! }
! else
! { Moderator.current = PackingWithSpouse;
! "^Now that you have the map, you head downstairs together to
! finish packing for Paris...";
! }
! ];
! Replacement GoSub is exactly like Graham's, except that it checks
! with the moderator before allowing the player to make the movement
[ GoSub i j k df movewith thedir old_loc;
if (second ~= 0 && second notin Compass
&& ObjectIsUntouchable(second)) return;
old_loc = location;
movewith=0;
i=parent(player);
if ((location~=thedark && i~=location)
|| (location==thedark && i~=real_location))
{ j=location;
if (location==thedark) location=real_location;
k=RunRoutines(i,before); if (k~=3) location=j;
if (k==1)
{ movewith=i; i=parent(i);
}
else
{ if (k==0) L__M(##Go,1,i);
rtrue;
}
}
thedir=noun.door_dir;
if (ZRegion(thedir)==2) thedir=RunRoutines(noun,door_dir);
j=i.thedir; k=ZRegion(j);
if (k==3) { print (string) j; new_line; rfalse; }
if (k==2) { j=RunRoutines(i,thedir);
if (j==1) rtrue;
}
if (k==0 || j==0)
{ if (i.cant_go ~= 0) PrintOrRun(i, cant_go);
rfalse;
}
if (j has door) ! is the direction a door object? if so...
{ if (j has concealed) return L__M(##Go,2);
if (j hasnt open)
{ if (noun==u_obj) return L__M(##Go,3,j);
if (noun==d_obj) return L__M(##Go,4,j);
return L__M(##Go,5,j);
}
k=RunRoutines(j,door_to); ! where does it go?
if (k==0) return L__M(##Go,6,j);
if (k==1) rtrue;
j = k;
}
if (Moderator.locator(j)==1) rtrue; ! ask the moderator
if (movewith==0) move player to j; else move movewith to j;
location=j; MoveFloatingObjects();
df=OffersLight(j);
if (df~=0) { location=j; real_location=j; lightflag=1; }
else
{ if (old_loc == thedark)
{ DarkToDark();
if (deadflag~=0) rtrue;
}
real_location=j;
location=thedark; lightflag=0;
}
if (AfterRoutines()==1) rtrue;
if (keep_silent==1) rtrue;
LookSub(1);
];