Monday, July 14, 2014

Some non-Roodylib parent(player) code

It seems like I've been spending a lot of time this year making sure Roodylib handles parent objects as well as possible, and every so often, I find something to tweak.

The other day, I found myself thinking about the old player-in-a-closed-container situation.  For my purposes, I was coding a coffin in a room.  Once the player is in the coffin and closes it, I wanted only objects within the coffin to be within reach (and seen).

Of course, it doesn't work out of the box, and I went on this epic quest, updating DoOpen, DoClose, FindLight, ObjectIsLight, and FindObject.  I was attempting to code an elaborate system that tried to figure out if the player can be seen from the location and act accordingly.  Working against me was the fact that FindObject likes to take for granted that the player object can always be seen, no matter what room is being used with it.  I tried so hard to tweak it to work for all occasions, but some instance was always breaking.

It's probably for the best that it never worked because who wants to add all of that code, anyway?  Eventually, it occurred to me that the easiest thing an author could do was just to set the coffin as the location when it was closed (and the room to the location when it was opened again).  Here is some example code:


It's possible that I didn't think of every verbroutine instance to be covered when the player is in the container, but that should be a good start!


Closing the coffin without a light source
More coffin antics coming in the next post!!

Clothing Update Part II

So, all of this work with clothing made me go, huh, wonder if my old "AIF" code still works. That's the flag that allows you to take off all of your clothes easily.  I mean, I have no intention of writing any clothes-heavy (or clothes-light har har) games anytime soon, AIF or not, but the world model fetishist in me likes to know these things work.  Anyhow, I found that, no, my old code did not work (I don't think it ever worked perfectly; I just hadn't completely tested it correctly).

See, the big problem is that >REMOVE ALL should default to held items, but you can't give it a multiheld token because you still want REMOVE to work with things like platforms or containers, and if you give it just the multi token, it ignores held items completely (only working for held items when they are used specifically).

Initially, the only way I could get it to work was to use the USE_CHECKHELD flag.  The checkheld system moves everything the player is holding to a hidden container in the room, so everything is considered not held (but it marks them with the checkheld workflag so the library knows it WAS held), allowing all verbs to work as you'd like.

Still, I put in all of this work so that in the instances where REMOVE failed, I made WEAR and TAKE OFF also fail in the same way just so there'd be consistency. I also made some instances of DoGet get directed to DoTakeOff.  Anyhow, I'm sort of embarrassed by how much work went into it, but if you want to check it out for yourself, check out the routines ExcludeFromAll, ParseError (cases 3 and 9), and VerbHeldMode in the next official release of Roodylib (version 3.9).  It is really ugly code; I should probably clean it up, but I just don't want to even look at it for a while.

As it often goes, I figured out a nice, non-checkheld-system workaround the next day. It occurred to me that the following PreParse code would do the trick:


        if word[1] = "remove" and word[2] = "~all"
        {
            word[1] = "take"
            InsertWord(2,1)
            word[2] = "off"
            return true
        }
        return false

So, basically, >REMOVE ALL gets turned into >TAKE OFF ALL (since >TAKE OFF doesn't have the same grammar issues).  Of course, ParseError messages will say TAKE OFF instead of REMOVE, but I don't think most players would even notice it isn't intentional.

Right now, if the AIF flag is not set, all variations of TAKE OFF, REMOVE, or WEAR ALL get a "Please specify which item to <blank>."-type message.  If it's set, those commands work, although they work in slightly different ways, depending on whether USE_CHECKHELD is set.

(Of course, I jokingly call it the "AIF" flag, but an author could easily want such a flag in a roleplaying-flavored game where the PC changes his or her armor a lot.)

Clothing Update Part I

So, it feels like I've been working on Roodylib a lot over the last few weeks.  It'll be interesting when I put up the next official release and go over all of the updates one last time for the changelog.

Robb Sherwin has been looking to get his game Cryptozookeeper sold online at places such as Steam and Desura, so I've been looking over the code to suggest tweaks to make this or that more user-friendly to those not familiar with IF.  Anyhow, looking at some of the things that game does made me go, "oh yeah, that probably should be an option in Roodylib!" on several occasions.

One thing CZK does is list worn clothing items before listing the rest of the inventory.  I actually wrote that code in the first place, but I didn't like how it replaced SpecialDesc in such an awkward way.  I felt, this time around that, hey, Roodylib can do it better!

So, first off, Roodylib has a new LIST_CLOTHES_FIRST flag that you should set if you want to run the new code. Then, for any character you want clothing to be automatically listed for, you add this:


    list_contents
        return ListClothesFirst(self)

Interestingly, ListClothesFirst only returns true if the character is wearing all of their belongings (this keeps an extra space from being printed after the worn-clothing list).  If you have an NPC where you only want clothing and held items to be listed when you examined them (and not in room descriptions), you could do this:


    list_contents
    {
        if verbroutine = &DoLookAround
            return true
        else
            return ListClothesFirst(self)
    }


Here is a screenshots of this system in action:




So, let's take a look at how this works. First off, of course, there's that ListClothesFirst routine:


routine ListClothesFirst(char)
{
    local x,w,c, list_save, v
    v = verbroutine
    verbroutine = &ListClothesFirst
    list_save = list_count
    list_count = 0
    for x in char
    {
        if x is worn and x is clothing
        {
            w++
            list_count++
        }
        elseif x is not hidden
        {
            x is already_listed
            c++
        }
    }
    if w and c
        list_nest = 1
    if list_count
    {
        if v ~= &DoInventory
            Indent
        if v = &DoLook
            FORMAT = FORMAT | USECHARNAMES_F
        RLibMessage(&ListClothesFirst,1,char) ! "You are wearing"
        ListObjects(char)
        if FORMAT & USECHARNAMES_F
            FORMAT = FORMAT & ~USECHARNAMES_F
        else
            FORMAT = FORMAT & ~USECHARNAMES_F
        list_nest = 0
    }
    for x in char
    {
        if x is not worn or (x is not clothing and x is worn)
            x is not already_listed
        else
        {
            AddSpecialDesc(x) ! tags worn items as already_listed
        }
    }
    verbroutine = v
    list_count = list_save - w
    return (not c and w)
}

It checks to see if any objects are worn, lists them, and marks them as already_listed.  I also needed to add this bit to ListObjects:


The ClothingCheck routine makes sure that children of worn clothing items aren't listed a second time (as they were listed by the ListClothesFirst routine).  There's also some code to make sure character names are used when it might be confusing.

Anyhow, here is the ClothingCheck routine:


routine ClothingCheck(a)
{
#ifset LIST_CLOTHES_FIRST
    if (a is worn and a is clothing and verbroutine ~= &ListClothesFirst) or
        (a is not worn and verbroutine = &ListClothesFirst)
        return true
    else
#endif
        return false
}


As you see, it has to check the verbroutine (which ListClothesFirst temporarily sets).

Anyhow, part of the reason I break this all down is because it's very possible you'll want to split up your player's or characters' inventories in other ways, and hopefully this is enough of a blueprint to get you on your way.

(All of this will be in the next official Roodylib release... coming soon!)

Thursday, May 8, 2014

>EXAMINE HACKER'S KEYRING

One of the things I've wanted for Hugo was an automated way to support possessive adjectives when referring to characters' belongings.  Since Hugo doesn't really give you complete control over the grammar, this is pretty hard to do.  Recently, I coded the best solution I could come up with.  Basically, it's a wrapper routine for Acquire that sets or clears adjectives as needed.  The downside is, you'd have to add an extra empty element to any object that might be used with it, like this:

       

object potato "potato"
{
 article "a"
 adjective "hot" 0
 noun "potato"
 in you
}

       
 

Now, I couldn't really decide on an intuitive name for the routine, but since I figured it was for games where objects are passed around between characters a lot, I opted for the name "HotPotato":

       

attribute keep_adjectives
routine HotPotato(obj,new_parent)
{
 local a
 a = parent(obj)
 if a is living and obj is not keep_adjectives
 {
  if CharacterKey(a)
   obj.adjective #(obj.#adjectives) = 0
 }
 if not new_parent
  remove obj
 else
 {
  a = CharacterKey(new_parent)
  if Acquire(new_parent, obj) and obj is not keep_adjectives
  {
   if a
    obj.adjective #(obj.#adjectives) = a
  }
 
}
       
 

If not provided with a "new_parent" object, HotPotato just removes the object after clearing any applicable adjectives.  Any game that uses this would have to provide its own CharacterKey routine, which would look like this:

       
routine CharacterKey(char)
{
 local a
 select char
  case fred: a = "fred's"
  case player: a = "my"
 return a
}
       
 

Now, all of this work is kind of useless since among games with characters, there usually isn't so much object-swapping that most of this couldn't be done by hand.  Plus, you'll notice that HotPotato only checks for one layer of containment in characters and there might be games where you'd want to check for grandchildren (or further).  Still, this is as close to automating this in Hugo as it's going to get, I think.

There's also the issue of words like "your","my","his", and "her" (>ASK FRED ABOUT HIS HAT).  I find the best solution is to just define these words as removals so the parser completely ignores them.

       
removal "his","her"
removal "my","your"
       
 
How many of these you define as removals depends on your game (games like Spur use "your" as a permanent adjective for certain things).  Also, because of games like Spur where some objects keep a certain ownership despite who is carrying them (like, "your gun" compared to "O'Grady's gun"), I added that keep_adjectives attribute to give objects that should be left alone.

Monday, March 24, 2014

Roodylib 3.8

So, this release feels like it adds a lot more than it probably will feel to the end user, but here is the changelog:

* Added an "elevated" attribute for enterable platforms (such as ladders) where "down" is an accepted exit direction
* Changed the vehicle class again (check out roodylibdocs.odt)
* Added USE_SMART_PARENTS flag so if the player tries going into an invalid direction while in an enterable object, they get "You can't go that way." instead of "You'll have to get up/out."
* In doing so, updated DoGo, and the vehicle and door classes to support it
* Added SKIP_DOORS flag so player can breeze through unlocked doors without opening them like NPCs
* Added a HugoFix settings object so debug monitor settings are kept after a restart
* If daemon/fuse monitoring is on (in HugoFix), a prompt asks whether to run the relevant daemon/fuse (to make it easy to skip during testing, if wanted)
* Got rid of an unnecessary AUX_MATH routine call in printstatuslib
* Made sure Roodylib is more NO_OBJLIB, NO_VERBS, and NO_XVERBS compatible
* Added USE_DARK_ROOM flag so dark rooms appear like an actual room when you enter them (check out roodylibdocs.odt)
* Updated flags.h to include new flags
* Update "go" grammar
* Added the coverart extension to Roodylib because of its configlib compatibility
* Updated old files to have UNIX line feeds and fixed indenting on older code

I also touched up some of the documentation files to mention a couple more things.  Still, there is probably lots of stuff that I could describe more.  If there are Roodylib functions that you'd like to see be better described, shoot me an e-mail!

You can download the latest version here: http://roody.gerynarsabode.org/notdead/roodylib_suite.zip

Monday, January 20, 2014

Roodylib v 3.7

Of course, as soon as I submitted that last version, I remembered some other things I had meant to put in.  Luckily, a couple other things popped up along the way so this update isn't quite as minimal as it could be.

Get it here: http://hugo.gerynarsabode.org/index.php?title=Roodylib

v 3.7 -
        * Added CallFinish, MakePlayer, CoolPause, PauseForKey routines
* Updated shell files to make it easier to turn Roodylib off (for tracking errors down to original library code)
* Updated credits text

Monday, January 13, 2014

Roodylib version 3.6 release

Uploaded a new Roodylib.  Mainly, it fixes some things that Juhana Leinonen found in his Hugo dabbling.  Go download it from Hugo By Example or the Hugo Code Lab (up in the links section to the right).

v 3.6 - * Added DoOpen replacement
* Fixed a ParseError bug (caused by me, not original)
* Added DoGet replacement, updated CheckReach for dealing with instances where the player is in a closed enterable
object and is trying to get something else in the same object