Timesasked.h

Timesasked.h provides a system for keeping track of times that a character has been asked or told about various objects/topics. It also has some support for notifying the player of currently-available topics. Get it here.

The code

It is probably best explained by reading the commentary:

!::
! TIMESASKED Version 1.4 by Roody Yogurt
!::

!\
This library contribution tries to make keeping track of how many times you ask
or tell about an object easy, for use with games with elaborate ASK/TELL system.
It also supports listing currently-available topics.
\!

!\
Besides including this in your game, you'll need to to declare two constants
BEFORE this is included. The two constants are:

constant TOTALCHARACTERS # ! should hold total number of characters in the game
constant MAXTOPICS # ! total number of topics any character can be asked about

Next, any object that will have multiple responses (so we need to keep track
of times asked/told) should "inherit"  the topics class. Example:

player_character you "you"
{
   inherits topics ! we can ask characters about ourselves
}

Now, every character should inherit the "chartopics" class. Example:

character randomshmoe "Random Schmoe"
{
   inherits chartopics
}

Now, witihn a character's normal DoAsk/DoTell xobject code, you can get the
current number of times asked/told by calling Asked and Told respectively.
Add some code to increase the counter. Take a look at the included example
for assistance.

If you want to make use of the DoAskTellTopics routine which allows players to
see what objects can be currently asked and told about, add the following to
your verb grammar:
xverb			"topics"
*										DoAskTellTopics

If you want the topic listing to *work properly*, you'll want to make your own
CharRules routine. Filling it out with your characters and objects, you can
define rules under which circumstances objects are available. You can even set
it so one topic won't show up unless another object has been asked/told about
by checking Asked(<other object>) or Told(<other object>)

\!

#ifclear _TIMESASKED_H
#set _TIMESASKED_H

#ifset VERSIONS
#message "timesasked.h Version 1.4"
#endif

#if undefined TOTALCHARACTERS
constant TOTALCHARACTERS 10
#endif

#if undefined MAXTOPICS
constant MAXTOPICS 10
#endif

array asktopicsleft[MAXTOPICS]
array telltopicsleft[MAXTOPICS]

! some properties to hold these values
property timesasked
property timestold

attribute is_topic

! each NPC should get his or her own charnumber, starting at 1, ending at
! the TOTALCHARACTERS value
property charnumber

class topics
{
   timesasked #TOTALCHARACTERS
   timestold #TOTALCHARACTERS
   is is_topic
}

class chartopics
{
   charnumber 0
   inherits topics
}

! just a class object for use with non-physical
! conversation topics and such
class topic_object
{
   found_in
      return location
   before
   {
      object
      {
         "Don't be silly."
      }
      xobject
      {
         if verbroutine = &DoAsk,&DoAskQuestion,&DoTell,&DoTalk
            return false
         "Don't be silly."
      }
   }
   is known
}

object timesaskedlib "timesasked"
{
#ifset _ROODYLIB_H
   type settings
   in init_instructions
   did_print 0
   execute
   {
      if not CheckWordSetting("restore") and not CheckWordSetting("undo")
         SetCharNumbers
   }
#endif  ! _ROODYLIB_H
}

routine SetCharNumbers
{
   local i = 1
   local n
   while i <= objects
   {
      if i.#charnumber and i ~= chartopics
      {
         i.charnumber = ++n
      }
      i++
   }
}

!\ ChangeValueAsked - utility routine for manually changing the
'timeasasked' property for a specific character (not actually called
by anything \!
routine ChangeValueAsked(obj,char,value)
{
   obj.timesasked #(char) = obj.timesasked #(char) + value
}

routine Asked(obj)  ! only meant to be called by character's DoAsk code
{                   ! returns the 'times asked' number of the current object
                    ! being asked about
   if not obj
      obj = xobject
   return obj.timesasked #(speaking.charnumber)
}

routine Told(obj) ! only mean to be called by character's DoTell code
{                 ! returns the 'times told' number of the current object
                  ! being asked about
   if not obj
      obj = xobject
   return obj.timestold #(speaking.charnumber)
}


! ArrayCheck checks an object's property elements
! and returns true when it finds a value

routine ArrayCheck(arr)
{
   if array arr[0]
      return true
   else
      return false
}

!\ TopicsList is a modified version of PropertyList. "you" as a topic gets
changed to "yourself" and the speaker is listed as "himself" or "herself" \!

routine TopicsList(arr, artic)
{
   local a, b, n, total

   for (a=0; a< MAXTOPICS; a++)
   {
      if array arr[a]:  total++
   }

   for (a=0; a< MAXTOPICS; a++)
   {
      b = array arr[a]
      if b
      {
         if artic
            The(b)
         else
         {
            if b = speaking or b = player
               print b.pronoun #4;
            else
               Art(b)
         }

         if (a+1) < total and total > 2
            print ", ";
         elseif (a+1) = total-1:  print " ";

         if a+1 = total - 1
            print AND_WORD; " ";
         n++
      }
   }
   return n
}


routine DoAskTellTopics
{
   if not speaking
   {
      TimesAskedVMessage(&DoAskTellTopics,1) !	"You are not talking to anyone
                                             !   at the moment."
      return false
   }

   local i,j

! time to clear the character's asktopicsleft and telltopicsleft properties
!   so we can refill them

   for (i=0; i<MAXTOPICS; i++)
   {
      asktopicsleft[i] = 0
      telltopicsleft[i] = 0
   }

   verbroutine = &DoAsk
   for (i=29; i<=objects; i++)
   {
      if i is is_topic and CharRules(i)
         asktopicsleft[j++] = i
   }

   j = 0

   verbroutine = &DoTell
   for (i=29; i<=objects; i++)
   {
      if i is is_topic and CharRules(i)
         telltopicsleft[j++] = i
   }

   local m,n
   TimesAskedVMessage(&DoAskTellTopics,5) ! "("
   if ArrayCheck(asktopicsleft)
   {
      TimesAskedVMessage(&DoAskTellTopics,2) ! "You can ask <char> about..."
      m = 1
   }
   if ArrayCheck(telltopicsleft)
   {
      if m
         print " ";
      TimesAskedVMessage(&DoAskTellTopics, 3 , m) ! "You can tell <char>
                                                  ! about..."
      n = 1
   }
   if not n and not m
      TimesAskedVMessage(&DoAskTellTopics,4) ! "You have nothing to talk about
                                             !  at this time."
   TimesAskedVMessage(&DoAskTellTopics,5,1)  ! ")"
}

routine TimesAskedVMessage(r, num, a, b)
{
   if NewTimesAskedVMessages(r, num, a, b):  return

   select r
      case &DoAskTellTopics
      {
         select num
            case 1: "You are not talking to anyone at the moment."
            case 2
            {
               print "You can ask "; speaking.name ; " about ";
               TopicsList(asktopicsleft)
               print ".";
            }
            case 3
            {
               print "You can tell ";
               if a
                  print speaking.pronoun #2 ;
               else
                  print speaking.name ;
               print " about ";
               TopicsList(telltopicsleft)
               print ".";
            }
            case 4
            {
               print "You have nothing to talk about at this time.";
            }
            case 5
            {
               if not a
                  print "(";
               else
                  print ")"
            }
      }
}

!\ The NewTimesAskedVMessages routine may be REPLACED and should return
true if a replacement message exists for routine <r> \!

routine NewTimesAskedVMessages(r, num, a, b)
{
   return false
}

routine CharRules(obj)
{
   return true
}

#endif

Some examples

! Example of a character
character fred "Fred"
{
   noun "fred"
   in STARTLOCATION
   inherits chartopics ! inherits all relevant properties
   after
   {
      object DoAsk
      {
         local notcount ! let's create a variable that if true, the 'timesasked'
                        ! counter does NOT increase
         select xobject
            case you
            {
               if not Asked
                  "You seem swell."
               else
                  "I said you seem swell."
            }
            case STARTLOCATION
            {
               select Asked
               case 0 : "I like swell places."
               case 1: "This place seems swell."
               case 2: "Sure, this place seems really swell."
               case else: "I'm done talking about how swell this
               place is."
            }
            case fred: "I'm just this guy."
            case else: "I dunno!"
         if not notcount
            xobject.timesasked #(self.charnumber) =  Asked + 1
      }

      object DoTell
      {
         select xobject
         case you
         {
            if not Told
               "You seem swell."
            else
               "I said you seem swell."
         }
         case STARTLOCATION
         {
            select Told
            case 0 : "I like swell places."
            case 1: "This place seems swell."
            case 2: "Sure, this place seems really swell."
            case else: "I'm done talking about how swell this
            place is."
         }
         case self: "I'm just this guy."
         case else: "I dunno!"
         xobject.timestold #(self.charnumber) =  Told + 1
      }
   }
}

! Example of an object
object tincan "tin can"
{
   article "the"
   adjective "tin"
   noun "can"
   inherits topics
}

! CharRules example
replace CharRules(obj)  ! called by DoAskTellTopics
{
   local a, b
   if verbroutine = &DoAsk
      b = Asked(obj)
   else
      b = Told(obj)
   select speaking
      case fred
      {
         select verbroutine
            case &DoAsk
            {
               select obj
                  case you: a = b < 1
                  ! "you" will show up as a topic if it hasn't been
                  ! asked about
                  case STARTLOCATION: a = b < 3
                  ! the start room will show up as a topic if it has
                  ! been asked about less than three times
                  case fred: a = 1
                  ! fred will always show up as a topic
                  case else : a = 0
                  ! nothing else will show up as a topic
            }
            case &DoTell
            {
               select obj
                  case you: a = b < 1
                  case STARTLOCATION: a = b < 3
                  case fred: a = 1
                  case else : a = 0
            }
      }
   return a
}