Like the previous paragraph implied, SpeakTo, if you don't remember, handles commands to characters, like >CHARACTER, HELLO. If you just type >CHARACTER_NAME at a prompt, SpeakTo is also called directly.
I noticed another thing, too. The original SpeakTo has:
And then later in the routine:if not FindObject(char, location) { actor = player ParseError(11, char) return }
For a while, this tricked me into thinking that the engine, while smart enough to interpret ">CHARACTER, GO NORTH. GET THE THING. GO WEST., etc.", was *also* smart enough to recognize orders even when the character isn't in scope, which would allow for smart-sounding error messages like "You're trying to command someone who isn't here." Instead, my attempts to replicate this kept on giving me "Better start with a verb."! In the event of: >CHARACTER, GO NORTH. GET THE THING. GO WEST., etc. if not FindObject(char, location) { run char.order_response return true }
Eventually I realized it was just a mistake in SpeakTo, so I ended up taking out that first bit of extra code (since hey, we want cool order support right?).
The current state of SpeakTo:
replace SpeakTo(char)Oddly enough, though, the new code wasn't working with my newconverse.h extension. Multi-command orders were getting odd responses for the second action. This prompted me to scrutinize newconverse.h even further. Eventually, I tracked down the problem to my fancy undo shenanigans (where I have the option to show the player which command is being undo'ed). It turns out writing over the word array all willy nilly can have some drawbacks! I changed the code to not clear the word array (which I'm not sure why I did that in the first place).
{
local 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
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
}
Then, I thought I had broken this nifty feature where, in some circumstances, it was possible to undo and skip over an entire conversation. In the end, it'd turn out that I had forgotten that the feature only worked under very specific settings, but long before that, my solution was to strip newconverse.h of its undo code, making a standalone "undolib.h" library extension. The extension, besides supporting what's-being-undo'ed reminders, also has the ability to attempt to undo multiple times at once (say, an unwinnable game that can undo back to when it was winnable).
The main disappointment is that I was too lazy to make undolib.h not roodylib-dependent. newconverse.h, while supporting roodylib, doesn't require it. It doesn't require undolib.h, either, but if somebody wants it, they'll have to use roodylib for now.
Another thing wrong with newconverse.h was that I was over-writing several arrays (like, writing six elements to a five-element-array). Hopefully, the experience will make me more aware of the bad code that caused such problems, but it was a good lesson nonetheless. If your game is acting nonsensically wonky, there's a good chance you overwrote an array somewhere!
For a little while, I thought newconverse.h would use my opportune.h extension, too. It's an extension for starting little daemons where a global variable's value dictates special responses to actions. I call them "windows of opportunity."
My opportune.h was kind of limited, though, since it could only handle one opportunity at a time (and it really preferred that those opportunities lasted for one turn). So, I rewrote it to make it property-array-based instead of global-variable-based. I got it working to satisfaction, but in the end, I decided, you know, in this case, I really do just want a fuse. Now, in retrospect, I'm not sure if there's really much call for a multiple-opportunity, multiple-turn opportune.h at all, so I'll probably change it back to its original form.
So yeah, there's a new fuse in newconverse.h. The positive side of all of my tinkering is that newconverse is a lot more consistent overall. Disallowing UNDO and skipping conversations and allowing normal undo works pretty much the same between most newconverse.h-supported conversation menus, whether they're on the top of the screen or at the bottom.
Undolib.h and newconverse.h need a little more polishing before I upload the latest versions, but things are looking good.
No comments:
Post a Comment