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.





No comments:

Post a Comment