Two of my WIPs are either entirely or partially driven by a >GO TO [room] mobility system. The second one, which I just recently started working on, got me thinking that I should turn the idea into an extension (or at least a page on Hugo by Example). I haven't really perfected anything, but I'll share what I've got so far.
First off, before "hugolib.g" is included, I declare some new >GO grammar:
verb "go", "walk"
* DoGo
* "to"/"in"/"into"/"inside"/"through" anything DoGoTo
* "out" object DoGo
* "out"/"outside" DoExit
* anything DoGoTo
Now, since we're going to be giving rooms noun and adjective properties, making them refer-able, we'll replace the room class with something that redirects a lot of actions:
replace roomEDIT: It turns out the above is really bad code, as lots of verb routines (like DoGet) expect to have an object value, so Perform-ing them with no object actually makes you pick up the nothing object (and so on). Here is some better code (although it's kind of "blunt force"; maybe there's a more elegant solution):
{
type room
is static, light, open, known
before
{
object
{
if verbroutine = &DoGo, &MovePlayer,&DoLook, &DoGoTo
{
return false
}
object = 0
Perform(verbroutine)
return true
}
object DoLook
{
Perform(&DoLookAround)
}
}
}
replace roomWe'll also make an "exits" property that keeps track of which rooms are available from other rooms (in this game, >GO TO just works room-to-room; this is not intended to be a Magnetic Scrolls type >GO TO scenario).
{
type room
is static, light, open, known
before
{
object
{
if verbroutine = &DoMove,&DoGet, &DoDrop, &DoPutIn,
&DoEmptyOrGet, &DoGive, &DoShow, &DoWear, &DoTakeOff,
&DoEat, &DoDrink, &DoHit, &DoLookThrough, &DoLookUnder, &DoExit
{
"I didn't understand you. Try being more specific."
return true
}
elseif verbroutine = &MovePlayer,&DoLook, &DoGoTo
{
return false
}
elseif verbroutine = &DoGo, &DoEnter
{
Perform(&DoGoto, self)
return true
}
object = 0
Perform(verbroutine)
return true
}
object DoLook
{
Perform(&DoLookAround)
}
}
}
property exitsNow, our DoGoTo routine:
routine DoGoTo
{
if object = location
{
"You're already there!"
return false
}
if not FindObject(object, location)
{
if object.type = room
{
if InList(location,exits,object)
{
return MovePlayer(object)
}
else
{
if not location.cant_go
{
"You can't go that way."
}
return false
}
}
}
return Perform(&DoGo,object)
}
A "here" object to redirect things like >GO TO HERE to the current location:
object here_object "here"An example room:
{
noun "here"
before
{
object
{
Perform(verbroutine, location)
}
xobject
{
Perform(verbroutine, object, location)
}
}
in_scope you
is known
}
room STARTLOCATION "Start Location"
{
adjective "start"
noun "location"
exits pond, barn
}
So, that's basically it. An issue: one of the things my code does is make every room known so that MovePlayer works. If you still want a sense of discovery in your games, you could have DoGoTo automatically make rooms listed in the exits property known (so that rooms 2 connections away might still be a mystery).
Anyhow, I don't think the code is ready for HbE yet, but I thought people might find it interesting just the same. Plus, hey, this blog needs all of the posts it can get.
I'm sure your code is much more solid than mine could ever be, but since you don't feel that your code is completely ready yet, I'll post my own approach, just in case it may give you any ideas.
ReplyDeleteThe code is based on scenery objects representing the locations that can be visited from another room. For example, the psuedo_kitchen object has the noun "kitchen", and its found_in property puts it in in the two rooms from which the player can enter the kitchen, as well as in the kitchen room itself, just to catch the case of someone trying to "ENTER KITCHEN" from the kitchen. (This code is based on the command ENTER rather than GO TO, but I think this method could be implemented with GO TO.)
scenery pseudo_kitchen "kitchen"
{
noun "kitchen"
article "the"
found_in foyer, kitchen, living_room
before
{
object DoEnter
{
FindHouseDir(kitchen)
}
}
}
And here is the routine that is called whenever the player tries to enter a "pseudo" object:
routine FindHouseDir(entering)
{
if entering = location
{
"You're already in ";
select entering
case kitchen
{
print The(pseudo_kitchen); "."
}
case foyer
{
print The(pseudo_foyer); "."
}
case living_room
{
print The(pseudo_living_room); "."
}
}
else
{
select location
case foyer
{
if entering = kitchen
{
Perform(&DoGo, e_obj)
}
elseif entering = living_room
{
Perform(&DoGo, n_obj)
}
}
case kitchen
{
if entering = foyer
{
Perform(&DoGo, w_obj)
}
elseif entering = living_room
{
Perform(&DoGo, nw_obj)
}
}
case living_room
{
if entering = foyer
{
Perform(&DoGo, s_obj)
}
elseif entering = kitchen
{
Perform(&DoGo, se_obj)
}
}
}
}
My method was only based on a specific case, and I'm not sure if it can easily be made more generic. I'll just throw it out there just on the off chance that any of my ideas might work well within your solution. :)
Thanks for contributing. Throwing one's own hat into the ring is always a good way to get to know Hugo better. Plus, it's always good to be reminded of the different ways one can approach a problem.
DeleteI only said my code wasn't ready for primetime since I felt like I hadn't adequately barraged it with problematic scenarios. That said, I think it's a good start.
Your found_in method is also a viable way to do it. Personally, I would lessen how much code I repeat as much as possible by creating a class object (even if the only identical code between the various "fake rooms" would be their before properties). In FindHouse, I'd also probably save myself the trouble of entering all of the various directions (Perform(&DoGo,se_obj)) and just call MovePlayer directly (MovePlayer(kitchen)).
I'd probably also bulk up the before property to make sure it has adequate responses to more commands.
That all said, yeah, that's a fine way to do it, especially for a smaller game where one doesn't mind entering all of the specific info.