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.
Wednesday, September 26, 2012
More PrintStatusLine stuff
Today, I got around to applying my new PrintStatusLine design to status-line-changing library extensions like newautomap.h and newconverse.h. It took some tweaking, but now with the current version of RoodyLib, it involves no finessing at all to have glk-enabled automap working in a game along with top-screen conversation menus. Just because I feel like my PrintStatusLine core code has changed just enough, I'm going to share the entire thing again. It might make some more sense if you are looking at a status-window-drawing extension along with this (like newautomap.h in http://roody.gerynarsabode.org/hbe/newautomap.zip), but maybe you can get some idea through the comments below:
! find_height - property that points to routines for determining status window height
property find_height alias u_to
! draw_window - routines with instructions for drawing the status window
property draw_window alias e_to
!\ bottom_justified - have this return true for status windows where regular
status information shares a window with other information and you want the
regular status information to be printed at the bottom of the window \!
property bottom_justified alias d_to
! terp_type - this property gets set to the current interpreter type automatically
property terp_type alias w_to
!\ status_override - normally, the status window object with the highest
find_height number gets drawn (so have those properties return 0 when not in
use), but sometimes more than one extension *could* be drawn, so we use
status_override to have one override the other (have it return true to initiate
such override) \!
property status_override alias nw_to
!\ chosen_window - set to the window instructions object whose draw_window
property will be executed. authors can ignore this. \!
property chosen_window alias nw_to
! "terp_type" values 0, 2, 4
enumerate step * 2
{
NORMAL_TERP, GLK_TERP = 2, SIMPLE_TERP
}
!\ a PrintStatusLine object in which we will put our instruction objects in (by
inclusion of extensions and what not. PrintStatusLine will call this directly).
\!
object printstatuslib
{
find_height
{
local highest, i, a
for i in self
{
a = i.find_height
if i.status_override
{
self.chosen_window = i
highest = i.find_height
break
}
if higher(highest,a) = a
{
self.chosen_window = i
highest = i.find_height
}
}
return highest
}
draw_window
{
run (self.chosen_window).draw_window
}
chosen_window 0
terp_type NORMAL_TERP
bottom_justified 1
}
replace PrintStatusline
{
local newstatusheight
! set the "terp_type" value
if IsGlk
printstatuslib.terp_type = GLK_TERP
elseif system(61) ! minimal port
printstatuslib.terp_type = SIMPLE_TERP
else
printstatuslib.terp_type = NORMAL_TERP
#ifset CHEAP
if cheap and printstatuslib.terp_type ~= SIMPLE_TERP
{
#if defined GAME_TITLE
CenterTitle(GAME_TITLE,0,1)
#endif
#if undefined GAME_TITLE
CenterTitle(CheapTitle,0,1)
#endif
display.needs_repaint = false
return
}
elseif cheap
return
#endif ! CHEAP
! figure out the size our window will be
newstatusheight = printstatuslib.find_height
! clear/remove the window if the status window height has changed
if (newstatusheight < display.statusline_height) and not system(61)
{
window display.statusline_height
{cls} ! clear whatever's there
window 0
}
display.statusline_height = newstatusheight
Font(BOLD_OFF | ITALIC_OFF | UNDERLINE_OFF | PROP_OFF)
window display.statusline_height
{
if printstatuslib.terp_type ~= SIMPLE_TERP
{
color SL_TEXTCOLOR, SL_BGCOLOR
cls
locate 1,1
}
run printstatuslib.draw_window
}
color TEXTCOLOR, BGCOLOR, INPUTCOLOR
Font(DEFAULT_FONT)
}
! Here is an example status window instruction object. It and its routines
! draw a regular status window
object statuswindow
{
in printstatuslib
find_height
{
return (call &FindStatusHeight)
}
draw_window
{
return (call &WriteStatus)
}
}
!\ Note: These properties *could* just say "return FindStatus". I just used
the above syntax to give a clue as to how one would change a value "mid-game".
If you want statuswindow.find_height to point to *another* routine, you could
have a line like this:
statuswindow.find_height = call &FindNewStatusHeight \!
! routine for finding the height of the regular status info
routine FindStatusHeight
{
local a, b
text to _temp_string
! can't start off a string with a space, it seems
!if not location ! so we'll save this space-writing code for the
! print "\_"; ! "status-writing" routine
!else
if not light_source
print "In the dark";
else
{
print capital location.name;
if FORMAT & DESCFORM_F
print "\_";
}
text to 0
a = StringLength(_temp_string)
text to _temp_string
select STATUSTYPE
case 1 : print number score; " / "; number counter;
case 2 : print HoursMinutes(counter);
case 3 : print "Score: "; number score; "\_ "; "Moves: "; number counter;
! STATUSTYPE case 3 is the "Infocom"-style status
case 4 : StatusType4 ! routine for configurable statusline
if (FORMAT & DESCFORM_F) and (printstatuslib.terp_type ~= GLK_TERP)
print "\_";
text to 0
if STATUSTYPE
b = StringLength(_temp_string)
if (b + a + 4)<display.screenwidth ! let's force a 4 character gap between
{ ! the two fields
return 1
}
elseif (b + a - 4 ) < display.screenwidth and STATUSTYPE = 3
{
text to _temp_string
print "S: "; number score; "\_ "; "M: "; number counter;
if (FORMAT & DESCFORM_F) and (printstatuslib.terp_type ~= GLK_TERP)
print "\_";
text to 0
return 1
}
else
return 2
}
! Roody's note: Replace this if you want to use the top right area
! for something else ("HUNGRY", "TIRED", or whatever)
routine STATUSTYPE4
{}
! routine for drawing the regular status
routine WriteStatus
{
if printstatuslib.bottom_justified and
printstatuslib.terp_type ~= SIMPLE_TERP
{
if statuswindow.find_height = 2
{
locate 1, (display.windowlines - 1)
}
else
locate 1, display.windowlines
}
if not location
print "\_";
elseif not light_source
print "In the dark";
else
{
if FORMAT & DESCFORM_F or (printstatuslib.terp_type = GLK_TERP)
print "\_";
print capital location.name;
}
if statuswindow.find_height = 1 and STATUSTYPE
{
print to (display.linelength - \
(StringLength(_temp_string) + \
((printstatuslib.terp_type = SIMPLE_TERP)*2) ));
StringPrint(_temp_string)
}
elseif STATUSTYPE and statuswindow.find_height = 2
{
if printstatuslib.terp_type ~= SIMPLE_TERP and
not printstatuslib.bottom_justified
locate 1, 2
else
""
if (FORMAT & DESCFORM_F) or (printstatuslib.terp_type = GLK_TERP)
print "\_";
StringPrint(_temp_string)
}
}
Monday, September 17, 2012
new debugging verb
The last time I was looking over roodylib's comments and documentation, I was reminded that I had added a new debugging verb (and had failed to mention it). Anyhow, it's called "verbtest", and it's pretty much a ripoff of something I saw that Juhana Leinonen wrote for Inform 7 the other year. Basically, you use it on an object, and it shows you the response to a ton of default verbs.
Here is an example transcript from a game I'm working on:
Maybe I'll incorporate it into HugoFix at some point (giving it a $vt command or something), but that seems a bit presumptuous for now.
Here is an example transcript from a game I'm working on:
>verbtest houseThe intent is that it'll remind authors of obvious commands that should have better responses.
Getting object:You can't take that.Wearing object:You can't wear the house.Listening to object:The house is not making a sound.Eating object:You can't eat the house.Drinking object:You can't drink the house.Hitting object:Venting your frustrations on the house won't accomplish much.Hello-ing object:That doesn't make any sense.Examining object:Dark and silent.Looking through object:You can't see through that.Looking under object:You don't find anything under the house.Go-ing object:No, it's time to go home.Entering object:No, it's time to go home.Sitting on object:No, it's time to go home.Exiting object:You're not in the house.Moving object:You can't move the house.Searching object:You don't find anything new.Smelling object:You don't smell anything unusual.>
Maybe I'll incorporate it into HugoFix at some point (giving it a $vt command or something), but that seems a bit presumptuous for now.
What's my PrintStatusLine?
I've been pretty good lately about coming up with roodylib improvements instead of working on my game. The last idea I had was especially challenging, and I have to admit, it took a couple days of pecking to truly get up the steam to finish it.
Now, I probably could have done it all with multiple routines and maybe some global variables and arrays, but like my other "settings" in roodylib, I decided to make it object based. Extension PrintStatusLine objects have properties that point to routines that determine the height they need and instructions for drawing their portion of the status window.
Even when you aren't using fancy library extension stuff, you could make the status window bigger than usual and draw whatever you want in the extra space (and you can make the normal status part of the window "bottom justified" if you want).
I just finished this thing so hopefully I'll be able to pretty it up a little so it's easier to understand at some point, but it's uncertain that this version of PrintStatusLine will be readable to anyone but me, unfortunately. Let's take a look at what I wrote:
So, say, if you're using newconverse's version of this (after I update it, that is), it'd point printstatuslib find_height and draw_window properties to either these routines or newconverse's status window instructions, depending on whether the player is in a conversation.
Hopefully, this isn't too complicated.
In a nutshell..
I've been annoyed that some of my library extensions have to replace PrintStatusLine. What do you do when two of your extensions replace PrintStatusLine? Of course, you hack together a version that works this way in some circumstances and that way in others. Like other parts of roodylib, I wondered if there wasn't a way to make it more modular.Now, I probably could have done it all with multiple routines and maybe some global variables and arrays, but like my other "settings" in roodylib, I decided to make it object based. Extension PrintStatusLine objects have properties that point to routines that determine the height they need and instructions for drawing their portion of the status window.
Even when you aren't using fancy library extension stuff, you could make the status window bigger than usual and draw whatever you want in the extra space (and you can make the normal status part of the window "bottom justified" if you want).
I just finished this thing so hopefully I'll be able to pretty it up a little so it's easier to understand at some point, but it's uncertain that this version of PrintStatusLine will be readable to anyone but me, unfortunately. Let's take a look at what I wrote:
property find_height alias u_to
property draw_window alias e_to
property bottom_justified alias d_to
property terp_type alias w_to
! "terp_type" values 0, 2, 4
enumerate step * 2
{
NORMAL_TERP, GLK_TERP = 2, SIMPLE_TERP
}
object printstatuslib
{
find_height
{
local sum, i
for i in self
{
sum += i.find_height
}
return sum
}
draw_window
{
local i
for i in self
{
run i.draw_window
}
}
terp_type NORMAL_TERP
bottom_justified 0
}
! this object and its properties draw the status window as we normally know it
object statuswindow
{
in printstatuslib
find_height
{
return (call &FindStatusHeight)
}
draw_window
{
return (call &WriteStatus)
}
}
replace PrintStatusline
{
local newstatusheight
#ifset CHEAP
if cheap
return
#endif
! set the "terp_type" value
if IsGlk
printstatuslib.terp_type = GLK_TERP
elseif system(61) ! minimal port
printstatuslib.terp_type = SIMPLE_TERP
else
printstatuslib.terp_type = NORMAL_TERP
! figure out the size our window will be
newstatusheight = printstatuslib.find_height
! remove the windows if the status window height has changed
if (newstatusheight < display.statusline_height) and not system(61)
window 0
display.statusline_height = newstatusheight
Font(BOLD_OFF | ITALIC_OFF | UNDERLINE_OFF | PROP_OFF)
window display.statusline_height
{
color SL_TEXTCOLOR, SL_BGCOLOR
if printstatuslib.terp_type ~= SIMPLE_TERP
{
cls
locate 1,1
}
run printstatuslib.draw_window
}
color TEXTCOLOR, BGCOLOR, INPUTCOLOR
Font(DEFAULT_FONT)
}
! routine for finding the height of the regular status info
replace FindStatusHeight
{
local a, b
text to _temp_string
if not location
print "\_";
elseif not light_source
print "In the dark";
else
{
if FORMAT & DESCFORM_F: print "\_";
print capital location.name;
print "\_";
}
text to 0
a = StringLength(_temp_string)
if STATUSTYPE = 1
{
text to _temp_string
if (FORMAT & DESCFORM_F)
print "\_";
print number score; " / "; number counter;
if (FORMAT & DESCFORM_F)
print "\_";
text to 0
}
elseif STATUSTYPE = 3
{
text to _temp_string
if (FORMAT & DESCFORM_F) : print "\_";
print "Score: "; number score; "\_ "; "Moves: "; number counter;
if (FORMAT & DESCFORM_F) : print "\_";
text to 0
b = StringLength(_temp_string)
}
elseif STATUSTYPE = 2
{
text to _temp_string
if (FORMAT & DESCFORM_F) : print "\_";
print HoursMinutes(counter);
if (FORMAT & DESCFORM_F) : print "\_";
text to 0
}
elseif STATUSTYPE = 4
{
text to _temp_string
if (FORMAT & DESCFORM_F) : print "\_";
STATUSTYPE4 ! routine for configurable statusline
if (FORMAT & DESCFORM_F) : print "\_";
text to 0
}
if (b + a + 4)<display.screenwidth ! let's force a 4 character gap between
{ ! the two fields
return 1
}
elseif (b + a - 4 ) < display.screenwidth and STATUSTYPE = 3
{
text to _temp_string
if (FORMAT & DESCFORM_F) : print "\_";
print "S: "; number score; "\_ "; "M: "; number counter;
if (FORMAT & DESCFORM_F) : print "\_";
text to 0
return 1
}
else
return 2
}
! Roody's note: Replace this if you want to use the top right area
! for something else ("HUNGRY", "TIRED", or whatever)
replace STATUSTYPE4
{}
! routine for drawing the regular status
routine WriteStatus
{
if printstatuslib.bottom_justified and
printstatuslib.terp_type ~= SIMPLE_TERP
{
if statuswindow.find_height = 2
{
locate 1, (display.windowlines - 1)
}
else
locate 1, display.windowlines
}
if not location
print "\_";
elseif not light_source
print "In the dark";
else
{
if FORMAT & DESCFORM_F: print "\_";
print capital location.name;
}
if statuswindow.find_height = 1 and STATUSTYPE
{
print to (display.screenwidth - \
(StringLength(_temp_string) + \
((printstatuslib.terp_type = SIMPLE_TERP)*2)));
StringPrint(_temp_string)
}
elseif STATUSTYPE and statuswindow.find_height = 2
{
if printstatuslib.terp_type ~= SIMPLE_TERP and
not printstatuslib.bottom_justified
locate 1, 2
else
""
StringPrint(_temp_string)
}
}
So, say, if you're using newconverse's version of this (after I update it, that is), it'd point printstatuslib find_height and draw_window properties to either these routines or newconverse's status window instructions, depending on whether the player is in a conversation.
Hopefully, this isn't too complicated.
One other thing...
I also threw together an extension for score notification, as I never have these things around when I need them. I'll add proper documentation and upload it somewhere at some point:!::Actually, I haven't even compiled that code yet, so there might be a typo or other kind of bug in there.
! Hugo Score Notification extension
!::
!\
Provides text like "You score has gone up by [x] points!"
\!
#ifclear _SCORENOTIFY_H
#SET _SCORENOTIFY_H
#ifset VERSIONS
#message "ScoreNotify.h Version 0.5"
#endif
property score_notify alias d_to
property points alias e_to
object scorenotifylib "scorenotify"
{
score_notify true
points 0
#ifset _ROODYLIB_H
save_info
{
select self.score_notify
case 0 : SaveWordSetting("score_off")
case 1 : SaveWordSetting("score_on")
return true
}
type settings
in init_instructions
execute
{
local a
a = CheckWordSetting("scorenotify")
if a
{
select word[(a-1)]
case "score_off": self.score_notify = 0
case "score_on": self.score_notify = 1
}
}
#endif
#ifset _NEWMENU_H
usage_desc
{
"\BSCORE NOTIFICATION ON\b- Be notified when you score points."
Indent
"\BSCORE NOTIFICATION OFF\b- Play without score notifications."
}
#endif ! NEWMENU
}
#ifset _ROODYLIB_H
object scorenotifymain
{
type settings
in main_instructions
execute
{
ScoreNotify
}
}
#endif ! _ROODYLIB_H
routine ScoreNotify
{
if scorenotifylib.points and scorenotifylib.score_notify
{
""
Font(BOLD_ON)
ScoreNotificationMessage(&ScoreNotify, 1, scorenotifylib.points ) ! "[Your score has gone up.]"
Font(BOLD_OFF
}
score += scorenotifylib.points ! add the points to the score
scorenotifylib.points = 0 ! reset the point counter
}
! routine to call for the last score of a game (after the winning move), as
! main is not called again
routine LastScore(a)
{
score += a
}
! otherwise, call this routine to add to the game score
routine AddScore(a)
{
scorenotifylib.points += a
}
routine DoNotifyOnOff
{
if scorenotifylib.score_notify
Perform(&DoNotifyOff)
else
Perform(&DoNotifyOn)
}
routine DoNotifyOn
{
if scorenotifylib.score_notify
ScoreNotificationMessage(&DoNotifyOn, 1 ) ! "[Score notification already on.]"
else
{
ScoreNotificationMessage(&DoNotifyOn, 2 ) ! "[Score notification on.]"
scorenotifylib.score_notify = 1
}
}
routine DoNotifyOff
{
if not scorenotifylib.score_notify
ScoreNotificationMessage(&DoNotifyOff, 1 ) ! "[Score notification already off.]"
else
{
ScoreNotificationMessage(&DoNotifyOff, 2 ) ! "[Score notification off.]"
scorenotifylib.score_notify = 0
}
}
routine ScoreNotificationMessage(r, num, a, b)
{
if NewScoreNotificationMessages(r, num, a, b): return
select r
case &DoNotifyOn
{
select num
case 1: "[Score notification already on.]"
case 2: "[Score notification on.]"
}
case &DoNotifyOff
{
select num
case 1: "[Score notification already off.]"
case 2: "[Score notification off.]"
}
case &ScoreNotify
{
select num
case 1 : "[Your score has gone up.]"
}
}
!\ The NewScoreNotificationMessages routine may be REPLACED and should return
true if a replacement message exists for routine <r> \!
routine NewScoreNotificationMessages(r, num, a, b)
{
select r
! case &ScoreNotify
! {
! select num
! case 1
! {
! print "[Your score has gone up by "; number a; " points.]"
! }
! }
case else : return false
return true ! this line is only reached if we replaced something
}
#endif _SCORENOTIFY_H
Friday, September 14, 2012
library suggestions
So far, roodylib replaces 54 standard library routines or objects (that number might be off by some; I sort of counted carelessly by hand). Some of these cover justified inadequacies or bugs uncovered by others; some are only replaced since an object something inherits from was also replaced. Still, yet, there are those that I replace because I feel there is something wrong with the design of something, and as it is clear that much of Hugo is the result of fine reasoning, it is likely that my changes have flaws I have not yet considered.
Anyway, let's talk about some of these kinds of changes. For starters, I replaced the female_character class. In the standard library, that class gets its own type ('female_character"), so if you have a game where you are checking if objects are NPCs, you have to check for both "character" and "female_character". I changed it so female_character objects are of type "character", as you can always check for the female attribute if you are truly looking for a female NPC.
More recently, I was writing some code that assumed the player_character object was of type "player_character". Funnily enough, this was not the case (it is actually of type "character")! Now, maybe people won't agree with me on this one, but I feel the player_character should get its own type, so in roodylib, it currently does so.
Much further back, I changed up some of the vehicle class code so it'd be (hopefully) easier to change which directions can exit a vehicle:
Lately, I also was looking at something whose design I disagree with but will do nothing about. I was taking a look at GetInput, which is a short and simple routine:
So yeah, that.
In other news, I updated Activate, the routine for starting daemons, so it complains if it is called before the player global has been set (I've run into that problem once or twice):
Anyway, let's talk about some of these kinds of changes. For starters, I replaced the female_character class. In the standard library, that class gets its own type ('female_character"), so if you have a game where you are checking if objects are NPCs, you have to check for both "character" and "female_character". I changed it so female_character objects are of type "character", as you can always check for the female attribute if you are truly looking for a female NPC.
More recently, I was writing some code that assumed the player_character object was of type "player_character". Funnily enough, this was not the case (it is actually of type "character")! Now, maybe people won't agree with me on this one, but I feel the player_character should get its own type, so in roodylib, it currently does so.
Much further back, I changed up some of the vehicle class code so it'd be (hopefully) easier to change which directions can exit a vehicle:
replace vehicle(DoGo also has some slightly different code, I believe)
{
type vehicle
vehicle_verb "drive" ! default verb
prep "in", "out" ! " prepositions
vehicle_move true ! by default, always ready to move
#ifclear NO_VERBS
before
{
parent(player) DoGo
{
if word[2] = "out" and object = self
{
object = out_obj
return false
}
if (object ~= u_obj, out_obj) and object.type = direction
{
! "To walk, you'll have to get out..."
OMessage(vehicle, 1, self)
return true
}
else
return object
}
}
#endif
is enterable, static
}
Lately, I also was looking at something whose design I disagree with but will do nothing about. I was taking a look at GetInput, which is a short and simple routine:
routine GetInput(p)Personally, I'd think that you'd only call GetInput when you want to also provide a prompt, so it should be something like this:
{
print p;
input
}
routine GetInput(p)My logic is, hey, only call GetInput when you want a prompt of some point. Otherwise, why not just call input directly? Thing is, the standard library (and other existing code) call GetInput without a prompt argument a lot. I could either change all of those library routines (breaking lots of game source, too, in the process, probably) or just ignore it. I'll just do the latter. I could add a slightly-similar-sounding routine to roodylib that behaves like I feel GetInput should, but hey, I admit this isn't an important problem...
{
if p
print p;
else
print prompt;
input
}
So yeah, that.
In other news, I updated Activate, the routine for starting daemons, so it complains if it is called before the player global has been set (I've run into that problem once or twice):
replace Activate(a, set) ! <set> is for fuses onlySo yeah, that's all some stuff.
{
local err
if not player
{
Font(BOLD_ON)
print "[WARNING: The player global must be set before
daemon (object "; number a;") can be activated.]"
err = true
}
a.in_scope = player
a is active
if a.type = fuse and not err
{
if set
a.timer = set
run a.activate_event
}
elseif a.type = daemon and not err
{
if set and not a.#timer
{
Font(BOLD_ON)
print "[WARNING: Attempt to set nonexistent timer
property on daemon (object "; number a; ")]"
err = true
}
else
a.timer = set
run a.activate_event
}
elseif not err
{
Font(BOLD_ON)
print "[WARNING: Attempt to activate non-fuse/\
daemon (object "; number a; ")]"
err = true
}
#ifset DEBUG
if debug_flags & D_FUSES and not err
{
print "[Activating "; a.name; " "; number a;
if a.type = fuse
print " (timer = "; number a.timer; ")";
print "]"
}
#endif
if err
{
Font(BOLD_OFF)
"\npress a key to continue..."
HiddenPause
}
return (not err)
}
Subscribe to:
Posts (Atom)