Saturday, August 29, 2020

>FOLLOW SO-AND-SO "Which way did he go?"

 To be honest, returning to game design hasn't been going all that well, so recently, I thought I could distract myself by looking over an old Roodylib "to do" list to see if there was anything I still wanted to add.

The first thing that sounded appealing was trying to come up with a solution to Hugo's default handling of >FOLLOW, as a game seems kind of dumb when you have just seen a character leave a room and >FOLLOW responds with "Which way did he go?"

I wrote a system that relies on using CharMove to move your NPCs.  The code takes note of the direction the NPC left in, and unless the NPC returns at some point, >FOLLOW will result in going in the same direction.  There was no ultra-clean method that didn't involve replacing some routines, but this was the most elegant solution that I could come up with.  Beyond including this code, authors would just need to remember to give their roaming NPCs the "last_dir" property that I have defined.


!::
! Smarter >FOLLOW SO-AND-SO behavior
!::
! If your game has more than 3 characters that roam and can be followed,
! define this constant as something higher
#if undefined MAX_FOLLOWABLE_CHARACTERS
constant MAX_FOLLOWABLE_CHARACTERS 3
#endif
array followable_characters[MAX_FOLLOWABLE_CHARACTERS]
! It is important that you add a last_dir property to any character
! that can be followed
property last_dir
#ifclear _VERBSTUB_H
routine DoFollow
{}
#endif
replace DoFollow
{
if object = player
{
if speaking
SpeakTo(speaking)
else
{
if player_person = 2
"Who are you talking to?"
else
{
"It's not obvious who you want ";
print player.pronoun #2;
" to talk to."
}
}
}
elseif object in location
print CThe(object); IsorAre(object, true); " right here."
elseif object.last_dir
return Perform(&DoGo, object.last_dir)
else
print "Which way did "; object.pronoun; " go?"
}
replace CharMove(char, dir)
{
#ifclear NO_OBJLIB
local newroom, a
general = 1
if dir.type ~= direction
return
if char in location
{
AddArrayValue(followable_characters,char)
char.last_dir = dir
}
newroom = parent(char).(dir.dir_to)
if newroom.type = door
{
a = newroom
newroom = a.between #((parent(char) = \
a.between #1) + 1)
if a is not open
{
if char in location or newroom = location
{
self = a
RlibOMessage(door, 3)
}
}
elseif newroom = location or char in location
a = 0
}
if char in location and not a and general = 1
{
Message(&CharMove, 1, char, dir)
event_flag = true
}
move char to newroom
if newroom = location
char.last_dir = 0
#ifset DEBUG
if debug_flags & D_SCRIPTS
{
print "["; CThe(char); IsorAre(char, true); " now in: ";
print capital parent(char).name; ".]"
}
#endif
if char in location and not a and general = 1
{
Message(&CharMove, 2, char, dir)
event_flag = true
}
elseif char in location
event_flag = true
#endif ! ifclear NO_OBJLIB
general = 0 ! always reset it
run parent(char).after
return true
}
routine AddArrayValue(arr, val)
{
local i
for (i=0; i< array arr[]; i++)
{
if array arr[i] = val
return i
elseif array arr[i] = 0
{
array arr[i] = val
return i
}
}
return false
}
replace BeforeRoutines(queue)
{
local r, i
if verbroutine ~= &MovePlayer
r = player.react_before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; player.name;
if debug_flags & D_OBJNUM
print " ["; number player; "]";
print ".react_before returned "; number r; "]\b"
}
#endif
return r
}
r = player.before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; player.name;
if debug_flags & D_OBJNUM
print " ["; number player; "]";
print ".before returned "; number r; "]\b"
}
#endif
return r
}
if verbroutine ~= &MovePlayer
r = location.react_before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; location.name;
if debug_flags & D_OBJNUM
print " ["; number location; "]";
print ".react_before returned "; number r; "]\b"
}
#endif
return r
}
r = location.before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; location.name;
if debug_flags & D_OBJNUM
print " ["; number location; "]";
print "before returned "; number r; "]\b"
}
#endif
return r
}
if verbroutine = &MovePlayer
r = object.before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; object.name;
if debug_flags & D_OBJNUM
print " ["; number object; "]";
print "before returned "; number r; "]\b"
}
#endif
return r
}
for i in location
{
if i ~= player
r = i.react_before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; i.name;
if debug_flags & D_OBJNUM
print " ["; number i; "]";
print ".react_before returned "; number r; "]\b"
}
#endif
return r
}
}
if verbroutine = &MovePlayer
{
ClearTrail
return
}
! queue is -1 if the object was a number (i.e., a literal digit)
if queue ~= -1 and xobject > display
{
r = xobject.before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; xobject.name;
if debug_flags & D_OBJNUM
print " ["; number xobject; "]";
print ".before returned "; number r; "]\b"
}
#endif
return r
}
}
if queue ~= -1 and object > display
{
r = object.before
if r
{
#ifset DEBUG
if debug_flags & D_PARSE
{
print "\B["; object.name;
if debug_flags & D_OBJNUM
print " ["; number object; "]";
print ".before returned "; number r; "]\b"
}
#endif
return r
}
}
}
routine ClearTrail
{
local n,i
while followable_characters[n] and n<MAX_FOLLOWABLE_CHARACTERS
{
i = followable_characters[n]
i.last_dir = 0
n++
}
}
view raw followchars.h hosted with ❤ by GitHub

After writing this, I wanted to test it out in a game.  I first tried "Guilty Bastards" because I incorrectly remembered being able to follow someone at some point (although it definitely has a character following you).  I then tried "Spur," which I was reminded was actually the game that inspired this whole better-following thing in the first place, but I couldn't even use my code with it as it completely substitutes another character script routine for CharMove.  I mean, sure, I could have rewritten it all so my code would still have worked, but in my sandbox version of "Spur", I had already written a >FOLLOW SO-AND-SO workaround anyway.

So I just had to test it with my own code, and hey, everything seems to be working fine.  I'll probably just throw it in the "extensions" folder in my Roodylib distribution at some point.





Saturday, April 11, 2020

4.2.1


So, I was testing the latest Roodylib with Juhana Leinonen's HugoJS interpreter, and it turned out that I had forgotten to incorporate some code from last fall which prevented Roodylib from causing games to hang.  Anyhow, it's in there now.

I also added an "opcode alternative" folder to the extensions so people can use Nikos Chantziaras' opcode-calling routine if they find Roodylib's less-direct method too confusing.

Yes, I skipped from 4.1.9 to 4.2.1 because even though I thought an April 4.2.0 release would be funny, I forgot about that whole Hitler thing and thought best to avoid it altogether, ha.

Roodylib 4.2.1 is here.  I've also updated the Hugo Notepad++ bundle and the Hugo Notepad++ add-on (for preexisting Notepad++ installations).

Sunday, April 5, 2020

Back From the Dead

So, first off, my apologies to anyone who was looking forward to more posts here during the last several years.  I was overwhelmed with some of the big changes I wanted to make to Roodylib to begin with, and then first my grandmother and then my father had health issues that took up all of my attention.  Sadly, they both passed away last year, but I'm thankful for all of the time I was able to spend with them.

I've had more time to get back to Roodylib in recent months, and I'm happy to be able to share the results.  I've uploaded a new version of Roodylib here.  I've also updated the Hugo Notepad++ bundle and the Hugo Notepad++ add-on (for preexisting Notepad++ installations).

As issue-tracking and version-numbering aren't really my forte, I have bumped this new release up to Roodylib 4.1.9, as it's basically an alpha I'd like to get out into the open (and my juvenile sense of humor thought it would be stupidly funny to put out a final 4.2.0 release later this month).  I've run it through a bunch of games with pretty much no issues except for changing how files are included, but one never can tell with these things.

Changes in this new release:

  • Right after the last official release, I noticed that Roodylib didn't handle multiple AGAIN/Gs in multi-command input.  Originally, I thought it was something I broke, but it seems that Hugo's library never handled it quite like I would want.  Anyhow, that's working now.
  • I moved whatever I could back to extensions for overall better code-readability for both Roodylib and the extensions themselves.  As much as I have loved the simplicity of just adding a flag to include functionality in my games, I decided that I have to keep future authors in mind so it's easier for them to see how each system works.  One side effect of this is that certain routines had to be broken up even further, so this new version of Roodylib grows in routine declaration despite being pared down in size.
  • Similarly, I redesigned some of the Hugofix stuff so extensions can easily add more debugging options without having to replace the entirety of some Hugofix routines.
  • I added some pronoun-choosing helper routines so games seem smarter.
  • Opcode functionality should be up to date and working on all existing opcode interpreters.
  • Improved some attachables stuff.
I didn't go all-out crazy making the documentation as perfect as possible, but a lot of this is covered in more detail in the Roodylib documentation.

There has been some other exciting Hugo news in recent months:
  • Juhana Leinonen released Borogove, which allows people to write Hugo games online (among several other types of game)!
  • Tristano Ajmone put The Hugo Book online.  With Kent's permission, updates and fixes have been made, making it the most accurate version of the book available.
  • Steps have been made to centralize the Hugo code base, and discussion of Hugo's future is underway.
So yes, exciting times.

Anyhow, to warn you, I don't really see myself updating Roodylib or this blog in the future with the same frequency as I did in years past.  While nothing is ever perfect, this version is basically the culmination of years-spanning intentions, and now that I've reached this point, it's likely that I'll move on to something else- whether that is back to game-writing or something else entirely, we shall see.