Wednesday, March 20, 2013

Describe-re-Place

I have to admit it. I love "to do" lists. This is one of the reasons I'll miss iGoogle when it's gone, as it provides a "to do" list app for the unobtrusive Chrome home page I have set up. That list is great, but I also have to admit that some of my most effective "to do" lists are just a couple text files I have on my desktop.

I've been revisiting those desktop lists lately to make sure I've accomplished everything listed there. I was able to delete a good deal of what they listed, but at least one of them reminded me of an unsuccessful bid to redesign DescribePlace into something that'd allow the addition of extra bits of text, like something that'd call that CanGo routine that lists room exits.

I like running DescribePlace with the DESCFORM_F format mask which gives room contents a sort of "double spaced" look to it. The heart of the DescribePlace problem is that you really need to know when stuff will be printed and when it will not to properly space it all.

Even though I really had no intention to return to the problem any time soon, I happened to lie down for a late afternoon nap yesterday, and almost instantly, I was hit by a DescribePlace replacement idea. Instead of having it be one routine, I thought I'd give it a main routine that calls routines that does the actual object listing. Then we have the benefit of returning true or false to determine whether or not to print extra lines.

I spent some time today putting the idea to action, and it already seems to be in a usable state. I think I even fixed some problems with the old replacement which required some elements to be listed after certain other ones. I have a feeling some of the routines could be more efficient or cleaner-looking, but that's for another day.

What does this mean for the average author? Well, since they probably won't touch anything, it doesn't really mean anything. Everything should work the same, although there are also some fixes in there for when using-DescribePlace-to-describe-a-room-that-isn't-your-location.

Like the old replacement, it should be helpful for games that like to change up the order in which things are listed, like Robb's games where characters are sometimes listed last. To change the order, just add something like this to your init or SetGlobalsAndFillArrays routine:
    DescribePlaceArray[0] = &CharsWithoutDescs, &ObjsWithoutDescs,
    &ParentofPlayer, &CharsWithDescs, &ObjsWithDescs, &AttachablesScenery
 (Except in whatever order you want)

Being so modular, it should also be easier to change the behavior of just how certain things are listed.

I also think that this will added to Roodylib, but I have to first decide if all of the new spacing behavior should fall under DESCFORM_F or if I need to come up with a new Infocom-spacing format mask called DESCFORM_I or something. I'll have to look at my old notes to see what I decided with that.

Anyhow, here's the code so far:
! This constant needs to be declared with something higher if you are
! adding additional elements into the mix
#if undefined DESCRIBEPLACE_ELEMENTS
    constant DESCRIBEPLACE_ELEMENTS 6
#endif

attribute already_printed
array DescribePlaceArray[DESCRIBEPLACE_ELEMENTS]

replace Describeplace(place, long)
{
    local obj,  didprint
    local need_carriage

    parser_data[PARSER_STATUS] &= ~PRONOUNS_SET

    ! Since, for example, a room description following entering via
    ! DoGo does not trigger before/after properties tied to looking
    ! around:
    !
#ifclear NO_VERBS
    if verbroutine = &MovePlayer ! was "if verbroutine ~= &DoLookAround"
    {
        if place is not visited and verbosity ~= 1
            return Perform(&DoLookAround)
        elseif long = true or verbosity = 2
            return Perform(&DoLookAround)
    }
#endif

    if not light_source
    {
        Message(&DescribePlace, 1)     ! "It's too dark to see..."
        return
    }

    place is known

    ! Print the name of the location as a heading
    Font(BOLD_ON)
    if not (FORMAT & DESCFORM_F)
        print "\n";

    print capital place.name;

    ! Print ", in <something>" if necessary
    if location = place and player not in place
    {
        if parent(player).prep
            print ", "; parent(player).prep; " ";
        elseif parent(player) is platform
            print ", "; ON_WORD; " ";
        else
            print ", "; IN_WORD; " ";
        print Art(parent(player))
    }
    print newline

    Font(BOLD_OFF)
    override_indent = false

    if place is not visited and verbosity ~= 1
    {
        if &place.initial_desc or &place.long_desc
            {
            didprint = true
            Indent
            }
        if not place.initial_desc
            run place.long_desc
    }
    elseif long = true or verbosity = 2
    {
        if &place.long_desc
            {
            Indent
            didprint = true
            }
        run place.long_desc
    }
    elseif place is not visited and verbosity = 1
    {
        if &place.initial_desc
            {
            Indent
            didprint = true
            }
        run place.initial_desc
    }

    if &place.list_contents and (FORMAT & DESCFORM_F) and didprint
        print ""        ! for double-space-after-heading formatting

    ! A location may contain an overriding listing routine, as may any
    ! parent, in the list_contents property
    !
    for obj in place
        obj is not already_printed

    if not place.list_contents
    {
        list_nest = 0
        local a
        while a < DescribePlaceArray[] and DescribePlaceArray[a]
        {
            need_carriage = call DescribePlaceArray[a](place)
            if (FORMAT & DESCFORM_F)
            {
                if need_carriage and didprint
                    ""
            }

            if need_carriage
            {
                need_carriage = false
                didprint = call DescribePlaceArray[a](place,true)
            }
            a++
        }
        print newline
        need_newline = false
    }
}

! routine for listing the player's siblings when he or she is in or on an
! object in the room
routine ParentofPlayer(place, for_reals)
{
    local obj
    if not for_reals
    {
        if player not in place and place = location
        {
            for obj in (Parent(player))
            {
                if obj is not hidden and obj ~= player
                    return true
            }
        }
        return false
    }
    else
    {
        list_nest = 1
        if WhatsIn(Parent(player))
            return true
    }
}

! routine for listing characters with short_desc descriptions
routine CharsWithDescs(place, for_reals)
{
    local obj
    if not for_reals
    {
        for obj in place
        {
            if (obj is not hidden and obj is living and
                 obj ~= player and ((&obj.short_desc and verbosity ~= 1) or
                (obj is not moved and &obj.initial_desc)) and
                obj is not already_printed )
            {
                return true
            }
        }
        return false
    }
    else
    {
        ! List all characters, if any
!        count = 0
        for obj in place
        {
            if obj is hidden or obj is not living or
                player in obj
            {
                obj is already_listed
            }
            else
            {
                obj is not already_listed
            }
        }
        local ret
        for obj in place
        {
            if obj is not already_listed
            {
                print newline
                if verbosity ~= 1 or (obj is not moved and &obj.initial_desc)
                    ShortDescribe(obj)
                if obj is already_listed  ! did we print something?
                {
                    ret = true
                    obj is already_printed
                }
            }
        }
        return ret
    }
}

! routine for listing characters without short_desc descriptions
routine CharsWithoutDescs(place,for_reals)
{
    local tempformat, count, obj
    if not for_reals
    {
        for obj in place
        {
            if (obj is not hidden and obj is living and
            obj ~= player and
            (not &obj.short_desc or
            (&obj.short_desc  and verbosity = 1)) and
            obj is not already_printed)
            {
                return true
            }
        }
        return false
    }
    else
    {
        for obj in place
        {
            if (obj is not hidden and obj is living and
            obj ~= player and
            (not &obj.short_desc or
            (&obj.short_desc  and verbosity = 1)) and
            obj is not already_printed)
            {
                obj is not already_listed
                count++
            }
            else
                obj is already_listed
        }
        if count ! list_count      ! if characters are to be listed
        {
            list_count = count
            tempformat = FORMAT
            FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F
            FORMAT = FORMAT | USECHARNAMES_F
            if (FORMAT & LIST_F)
            {
                FORMAT = FORMAT & ~LIST_F       ! clear it
                FORMAT = FORMAT | TEMPLIST_F
            }
            Indent
            list_nest = 0
            ListObjects(place)
            FORMAT = tempformat
            return true
        }
        return false
    }
}

! routine for listing objects with short_desc descriptions
routine ObjsWithDescs(place, for_reals)
{
    local obj, ret
    if not for_reals
    {
        for obj in place
        {
    #ifset USE_ATTACHABLES
        ! Exclude all attachables for now (and characters)
            if obj is not living and not obj.type = attachable and
                player not in obj and obj is not hidden and
                ((verbosity ~= 1 and &obj.short_desc) or
                (&obj.initial_desc and verbosity = 1)) and
                obj is not already_printed
    #else
            if obj is not living and player not in obj and
            obj is not hidden and
            ((verbosity ~= 1 and &obj.short_desc) or
                (&obj.initial_desc and verbosity = 1)) and
                obj is not already_printed
    #endif
            {
                ret = true
                obj is not already_listed
            }
            else
                obj is already_listed
        }
        return ret
    }
    else
    {
        for obj in place
        {
#ifset USE_PLURAL_OBJECTS
                    ! ...And don't list identical objects yet, either

            if (obj.identical_to).type = identical_class
            {
                if obj is not hidden
                    count++
            }
            elseif player not in obj
#else
            if player not in obj
#endif
            {
                if obj is not already_listed and
                    obj is not hidden and obj is not already_printed
                {
                    if verbosity ~= 1 or (verbosity = 1 and
                        (obj is not moved and &obj.initial_desc))
                        ShortDescribe(obj)
                    if obj is already_listed ! did we print something?
                    {
                        ret = true
                        obj is already_printed
                    }
                }
            }
        }
        return ret
    }
}

! routine for listing objects without short_desc descriptions
routine ObjsWithoutDescs(place, for_reals)
{
    local obj, tempformat, count
    if not for_reals
    {
        for obj in place
        {
#ifset USE_ATTACHABLES
            ! Exclude all attachables for now (and characters)

            if obj is not living and not obj.type = attachable and
                player not in obj and obj is not hidden and
                obj is not already_printed and
                (not &obj.short_desc or (&obj.short_desc and verbosity = 1))
#else
            if obj is not living and player not in obj and
            obj is not hidden and obj is not already_printed and
            (not &obj.short_desc or (&obj.short_desc and verbosity = 1))
#endif
                {
                return true
                }
        }
        return false
    }
    else
    {
        for obj in place
        {
#ifset USE_ATTACHABLES
            ! Exclude all attachables for now (and characters)

            if obj is living or obj.type = attachable or
                player in obj or (&obj.short_desc and verbosity ~= 1) or
                obj is already_printed
#else
            if obj is living or player in obj or (&obj.short_desc and verbosity ~= 1) or obj is already_printed
#endif
                obj is already_listed
            else
            {
                count++
                obj is not already_listed
            }
        }

        if count
        {
            list_count = count
            tempformat = FORMAT
            FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F
            if FORMAT & LIST_F
            {
                FORMAT = FORMAT & ~LIST_F       ! clear it
                FORMAT = FORMAT | TEMPLIST_F
            }
            Indent
            list_nest = 0
            ListObjects(place)
            FORMAT = tempformat
            return true
        }
        return false
    }
}

! routine for listing attachables and contents of scenery objects
routine AttachablesScenery(place, for_reals)
{
    local obj, ret
    if not for_reals
    {
#ifset USE_ATTACHABLES
        for obj in place
        {
            ! Print attachables last
            if obj.type = attachable and obj is not hidden
            {
                return true
            }
        }
#endif
        for obj in place
        {
            if obj.type = scenery
            {
                if player not in obj and
                    (obj is open or obj is not openable)
                {
                    local a
                    for a in obj
                        {
                        if a is not hidden
                            {
                            return true
                            }
                        }
                }
            }
        }
        return false
    }
    else
    {
#ifclear NO_OBJLIB
#ifset USE_ATTACHABLES
        for obj in place
        {
            ! Print attachables last
            if obj.type = attachable and obj is not hidden
            {
                ShortDescribe(obj)
                if obj is not already_listed
                    Message(&DescribePlace, 2, obj)
                obj = already_printed
                ret = true
            }
        }
#endif
        print newline
        override_indent = false

        ! Finally, list contents of scenery objects (unless we've
        ! already done so as the parent of the player)
        !
        for obj in place
        {
            if obj.type = scenery
            {
                obj is known
                if player not in obj and
                    (obj is open or obj is not openable)
                {
                    list_nest = 1
                    if WhatsIn(obj)
                        ret = true
                }
            }

            ! For scenery-derived objects that may change the type
            elseif obj is static, hidden
                obj is known
        }
        return ret
#endif  ! ifclear NO_OBJLIB
    }
}

No comments:

Post a Comment