Thursday, June 20, 2013

SpeakTo stuff

So, yesterday, I was adapting John Menichelli's Hugozork to Roodylib, just because I'm always curious to see how well Roodylib plays with already-written games. Initially, there was some ParseError replacement code for the Loud Room that I was able to move to Roodylib's PreParseError routine, but I noticed that some of it didn't do the Loud Room's echo behavior quite right so I spent some time fixing that.

While testing that code, I realized that the engine-called SpeakTo was skipping all of my glorious code completely (SpeakTo is called directly both when you give characters commands, like: >CLOWN, HELLO or just when you type an object with no verb, like >ROCK).

I decided that SpeakTo should have to do location and player before checks, to better allow response configurations for situations just like the Loud Room. Here is my current SpeakTo:
replace SpeakTo(char)
{
local a, v, TryOrder, IgnoreResponse, retval, stay, same, different
#ifset USE_CHECKHELD
if verbroutine = &DoDrop_CheckHeld
verbroutine = &DoDrop
elseif verbroutine = &DoPutIn_CheckHeld
verbroutine = &DoPutIn
#endif

#ifset VERBSTUBS
if verbroutine = &DoHelpChar and object = player
{
verbroutine = &DoHelp
object = nothing
}
#endif

#ifset USE_CHECKHELD
ResetCheckHeld
#endif

#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B[Speakto("; char.name;
if (debug_flags & D_OBJNUM)
print " ["; number char; "]";
print ") verbroutine="; number verbroutine;
print ", object="; object.name;
if (debug_flags & D_OBJNUM)
print " ["; number object; "]";
print ", xobject="; xobject.name;
if (debug_flags & D_OBJNUM)
print " ["; number xobject; "]";
print "]\b"
}
#endif

v = verbroutine
a = actor
actor = player
verbroutine = &SpeakTo
retval = player.before
actor = a

if retval
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; player.name;
if debug_flags & D_OBJNUM
print " ["; number player; "]";
print ".before returned "; number retval; "]\b"
}
#endif
return retval
}

retval = location.before
if retval
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; location.name;
if debug_flags & D_OBJNUM
print " ["; number location; "]";
print "before returned "; number retval; "]\b"
}
#endif
return retval
}

verbroutine = v

if char is not living
{
ParseError(6)  ! "That doesn't make any sense."
return
}

AssignPronoun(char)

! Handle player/typist-related ParseError messages:
if char = player
Message(&Speakto, 1)    ! "Stop talking to yourself..."
elseif not ObjectisKnown(object) and not FindObject(object, location)
ParseError(10, object)
else
   stay = true

if not stay
  {
  speaking = 0
  return
  }

if char is unfriendly
IgnoreResponse = true
else
{
! In the event of:  >CHARACTER, GO NORTH.  GET THE THING.  GO WEST., etc.
if not FindObject(char, location)
{
speaking = char
run char.order_response
return true
}

same = (char = speaking)

select verbroutine
case 0                  ! Just the character name is given,
! so just "X is listening."
{
if not char.order_response
Message(&Speakto, 2, char)
retval = true
}

#ifclear NO_VERBS
case &DoHello           ! Note the ampersands ('&')--or else
{                       ! the routines themselves would run
if not char.order_response
{
if char is not unfriendly
{
! "X nods hello."
Message(&Speakto, 3, char)
retval = true
}
else
{
IgnoreResponse = true
}
}
else
retval = true
}

case &DoAskQuestion
return Perform(&DoAsk, char, object)

case &DoTalk
{
if xobject
ParseError(6)
else
return Perform(&DoAsk, char, object)
}

case &DoTell
{
if object = player
return Perform(&DoAsk, char, xobject)
else
TryOrder = true
}
#endif  ! ifclear NO_VERBS

case else
{

! If the character can respond to a request, this should be dealt with by
! an order_response property routine; order_response--if it exists--should
! return false if there is no response for the given verbroutine

TryOrder = true
}
}

if TryOrder
{
if (not char.order_response)
IgnoreResponse = true
else
retval = true
}

different = (speaking ~= char)

! This same/different local variable stuff allows for certain
! orders to end conversations. If your order_response code clears
! the speaking global, this code prevents it being reset.

if retval and not (same and different)
speaking = char

if IgnoreResponse
{
if not char.ignore_response
Message(&Speakto, 4, char)      ! "X ignores you."
speaking = 0  ! clear the speaking global
}
return retval
}
Now, there are a couple problems. One, currently, both the location and player code have no idea what command the player is trying to order done, as I had to change the verbroutine to &SpeakTo for easy catching. Further complicating things, is that for the player object, I had to change the SpeakTo-determined actor global, too.

Ideally, I'll be able to figure out a way to organize all of these values so authors can have as clear an idea as possible about what was typed, and hopefully I'll be able to do it without the creation of new globals (although that isn't a big deal and I'll probably end up doing it anyway).

Also, let it be said that this new code is very lightly tested so there might be a gaping bug I haven't found yet.

2 comments:

  1. Oh, you must mean Hugozork. Cool, now we can play Zork with Roodylib if we want to. Hugozork was the first way that I played Zork at all, and I probably made it further into the game with Hugozork than I ever managed to get with Infocom's version of Zork I or the port of Dungeon.

    ReplyDelete
    Replies
    1. Thanks, yeah, I meant to say Hugozork. Fixed now.

      Delete