I think the latest versions of the library contributions discussed in the last couple posts are ready for the public:
NewConverse 2.0
Opportune 1.3
MultiOpportune 1.3
PastTense 1.2
Roodylib 2.0
The new UNDOlib extension is part of the roodylib suite (among several other added features). Opportune.h actually has decreased functionality compared to the earlier version; MultiOpportune.h is the version which does multiple opportunities, but like I said, fuses are probably better in that case. PastTense.h just has some bug fixes, but I've tested that one so little that I'm sure there are several more out there.
Thursday, October 18, 2012
Wednesday, October 17, 2012
array utility routines
In the previous post, I talked about how, for a while, I tried to do a "multiple opportunity" version of my "windows of opportunity" opportune.h extension. The final version ended up using property arrays, but originally, I approached the problem with regular arrays. I kept on wanting to do something and would think, huh, is there a routine for that? I'd then remember there wasn't and then would write it. I had mixed success.
The Good:
The Bad:
The Ugly:
If anything, I think this all is just a good reminder why property arrays can be favorable to regular arrays in these kinds of instances, as property arrays start at element 1 and therefore avoid the element-0 problem.
The Good:
routine ClearArray(array_to_be_cleared)Sometimes, hey, I like to clear arrays. Nothing wrong with a routine that saves me some time.
{
local n
for (n=0;n< array array_to_be_cleared[] ; n++ )
{
array array_to_be_cleared[n] = ""
}
}
The Bad:
routine AddArrayValue(arr, val)The problem with this one is that it doesn't distinguish from successfully placing the value in element 0 or being unable to place the value in an empty slot at all. It'd be slightly more successful if I just changed it to a return-true-on-success/return-false-on-failure thing.
{
local i
for (i=0; i< array arr[]; i++)
{
if array arr[i] = 0, ""
{
array arr[i] = val
return i
}
}
}
The Ugly:
routine InDisArray(arr, val)Bad pun aside ("in this array", get it?), this array-based version of InList has the same problem as AddArrayValue but is even more hampered by it, as returning-the-element-number is pretty important in such a routine. I could get around this by having element 0 return something else (like a constant called ZERO or the string "zero"), but it seems kind of dumb to make people remember this hack.
{
local i
for (i=0; i< array arr[] ; i++)
{
if array arr[i] = val: return i
}
}
If anything, I think this all is just a good reminder why property arrays can be favorable to regular arrays in these kinds of instances, as property arrays start at element 1 and therefore avoid the element-0 problem.
Tuesday, October 16, 2012
more SpeakTo speculation
I've been working again on my update to Christopher Tate's converse.h extension. I forget what the original issue was to get the ball rolling, but somewhere near the beginning was a point where I noticed >CHARACTER, GOODBYE commands (which, in my extension, is a viable way to end a conversation), on their success, was resetting the speaking global to the npc. I updated the extension's (and roodylib's) SpeakTo replacement to use local variables to check for certain changes of the speaking global during the execution of the routine. This is probably a useful-to-only-me feature, but it seems feasible to me that certain successful orders should end conversations (without sending the NPC to another room).
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:
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:
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.
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.
Subscribe to:
Posts (Atom)