Friday, November 16, 2012

space layout, the final frontier

When I released my first work of IF, I think the concept I was most lost with was how to do proper spacing in my game. There really aren't any guides on the different styles you can use for your game, and I found my game was really inconsistent and wished one existed. I eventually would learn that it boils down to "try to figure out the assumptions your language already makes and copy that." Still, even after all of these years, I can imagine some merit in such a guide.

Hugo, in particular, is especially confusing, as it introduces indented text, and there isn't really a large body of work out there to fully get how it's to be used, especially since it clashes with the Infocom games we grew up with. Over the years, I have come to respect the indentation system and appreciate the look it brings to the system. Just the same, if only to better understand the different styles, I've made it a quest to better support Infocom-style spacing in Hugo games.

Out of the box, Hugo can do some Infocom-style spacing things if you give the FORMAT global the DESCFORM_F flag. When that is set, the text in the status line is offset by a space, so it is not right up to the edge of the screen. Also, there's an extra line between the room description and the room contents listing.  My DescribePlace  replacement has furthered this, by including an extra line between every type of object in the room content listing.

A few days ago, I continued refining this "Infocom style Hugo mode", mainly because one of my WIPs is an homage to Infocom. The big change is that there is no longer an automatic empty line before a room listing. Coupled with the NOINDENT_F flag, it looks quite Infocom-y.

At first, I tied this behavior into DESCFORM_F, but I then decided that that was too much Infocom all of the time as I have come to appreciate the semi-Infocom look in some of my other games. I decided to give that behavior to a new flag that I called DESCFORM_I.

Anyhow, the new changes have been making me tweak this and that, as now it's up to any code that prints something before DescribePlace to also print an extra line. I'm liking the new behavior, but figure it'll be some time before this has settled in right among all extensions that call DescribePlace.

I think I'm due to upload several updates. I noticed some horrible behavior in the default menu for newmenu.h. I've since fixed it, but it's one of those things that makes you scratch your head and wonder how it got out the door in the first place. I think newautomap.h had some kind of update, too.

Anyhow, the work on this extension stuff has been kind of disheartening, what with all of the leaks that have popped up. I hope to get some good WIP progress done soon.

Monday, November 12, 2012

just WIP it

I spent a fair amount of time yesterday updating on older WIP to be roodylib-compatible. Initially, this meant more deleting than anything else, as it had a lot of the routine replacements and various code that were eventually incorporated into roodylib.

The first thing I learned from the process was that I was not happy with how newautomap.h interacts with newconverse.h, statusline-wise. I found myself wishing I had documented it better when I wrote my new PrintStatusLine replacements system. Eventually, I remembered that its current behavior has the map still show up when conversation options are present. Sure, the two windows were drawing fine, but in my false memory, I thought I had created a system to block the map from showing up at all under certain circumstances. In the end, I spent several hours tweaking newautomap.h to work better and do just that. I'm not sure it's the best-it-can-be yet, but it's better.

The other thing my old code reminded me about was how it had some code to allow clumping items-in-a-room's short_desc all to one paragraph, like:

      A giant spoon lies on the ground, as if discarded by some humongous spoon musician. Your friend, the giant raisin, is here, lying on its side.
 (the above is the clumping of two objects)

Traditionally, nice looking room prose like that has to be hardcoded into the room's long_desc property, but this little venture has me thinking that maybe I'll take this task on again. The way it's handled in my WIP right now is kind of ugly, but I'm thinking I might be able to handle the challenge a bit better now. We'll see.

Thursday, November 8, 2012

Thousands of Golf Balls

In my game, there's a part where you are in a room (street) where newspapers are being delivered. It occurred to me that if I used one object for both singular ("GET PAPER") and plural ("GET PAPERS") cases, there was a possibility of incorrect responses. Instead of checking this assumption or deciding to bulk up my newspaper object with parsing code that checks how it was referred to or splitting it into two hidden objects, I thought, hey, maybe I could actually do this with objlib.h's plural class.

Initially, this worked great, but I found that I could trick the game into showing me that there were only 4 newspaper objects with a command like >GET 5 NEWSPAPERS.

This also reminded me of an older WIP where, in a room full of golf balls, a tester tried the command >GET 1000 GOLF BALLS. Now, I'm not going to go code a thousand golf ball objects, but ever since then, I was a little disappointed that I couldn't do more to give the illusion that the game could handle such a command.

Anyhow, now that I have roodylib and a better understanding of how things work, I thought I'd take a swing at the problem. I tracked down the code in ParsePluralObjects that interprets the number in the command line. It calls WordIsNumber, which matches the word with a simple select-case that handles numbers "1"/"one" through "10"/"ten". I added code so it also calls StringToNumber, so it can now also handle 11-32767 (it can't handle the word version of these numbers but I figured that was a lost cause).

My first hurdle was that this section of code checks the word array for numbers twice, supposedly so that Hugo can understand commands like >GET TWO OUT OF THE THREE CHICKENS. My StringToNumber-enhanced routine can't be called twice like that, so I had to throw the "<blank> out of <blank>" code out.

It was easy enough to find the code in plural_class object where it prints a "There are only X objects here."-type command when you refer to too many, and it was also easy enough to change it to disregard the number of actual-objects-there.

Still, I thought there was another important number in the equation. There's the number you want the player to think is there. In the above example, I *don't* want >GET 1000 PAPERS to have the same reply as >GET 5 PAPERS. To handle this, I added a couple new properties to the plural_class object. One is called imaginary_plurals. It holds the number the plural class is supposed to have*.

* The type of plural object that uses these properties are going to be largely scenery. They aren't actually going to be picked up or interacted with to any large degree. We're only doing this to make responses sound smarter.

The other one is called over_max. It just holds the response the player will see when he refers to more objects than the imaginary plurals number.

Let's take a look at my finished newspaper object:
identical_class newspapers "newspapers"
{
    plural_of paper1, paper2, paper3, paper4
    noun "newspapers" "papers"
    single_noun "newspaper" "paper"
     imaginary_plurals 12
     over_max
        "Even with all of the apartments and homes on your street, you'd
     be lucky to find more than a dozen papers in the near vicinity."
}
 Hopefully, that'll all make sense in the end. There's a good chance I'm breaking more than I realized, as plural class stuff seems to juggle a lot. Anyhow, I think this *will* call for a new Roodylib upload. People can expect it soon enough.

Monday, November 5, 2012

"extra" extra_scenery

In the game-I've-most-recently-worked-on (I don't want to call it my "WIP" as I really should be starting on a HugoComp game soon), I got a little frustrated with the default handling of extra_scenery. The game in question has 2 or 3 words that should always result in "You don't need to refer to that.", on top of each room's additional list of words. I really didn't want to add those 2 or 3 words to every room's extra_scenery property, so I eventually just threw the entire list of words into the room class definition.

This was a pretty inelegant solution so I intended to try to come up with a better solution eventually. Well, that day ended up being yesterday, and here's what happened.

Originally, I tried to declare my own extra_scenery_words array and replace the room class's property array with a property routine that checked it. This was problematic.

The main thing was, Parse's extra_scenery checking code already checks every word in the word array against the extra_scenery property, so to find a match myself, I'd have to check every word array word against every extra_scenery_words word, so there's some wasted looping right there.

In the end, I thought the simplest solution was just to edit Parse and have it check the player object for extra_scenery words, too, so you can put your always-on extra_scenery there (this is also appropriate as I often use extra_scenery for player body parts that aren't implemented and such). The Roodylib  Parse routine now has this:
    for (a=2; a<=words and word[a]~="" and word[a]~="then"; a++)
    {
        if Inlist(player, extra_scenery, word[a])
        {
            Message(&Parse, 1)
            word[1] = ""            ! force ParseError(0)
            words = 0
            customerror_flag = true
            return true
        }
        elseif Inlist(location, extra_scenery, word[a])
        {
            Message(&Parse, 1)
            word[1] = ""            ! force ParseError(0)
            words = 0
            customerror_flag = true
            return true
        }
    }
(I'm not going to upload a new release of Roodylib just for this, but it'll be in the next version, of course)

I figure this'll take care of the majority of extra_scenery needs. If you find that you really need to tie extra_scenery behavior to an object, I also came up with this work around:
  1. Have your init routine set one of your player.extra_scenery property elements to a call to a routine:
    player.extra_scenery #3 = call &CheckStuff
  2. Write a routine for the above that returns a dictionary word under the right circumstances:
    routine CheckStuff
    {
        if FindObject(mess, location)
            return "stuff"
    }
That may be a little ugly, but hopefully your game doesn't have a lot of words like that!



Saturday, November 3, 2012

DoVerbTestAll update

In looking up my old post on DoVerbTest, I noticed that I had already posted about it when I last wrote about it. In any case, I recently added some code to DoVerbTestAll, the version that tests every object in a game. Since code-posting looks a bit better over there, I put it at Royce Odle's Hugo forum.

This new version tests some no-object verbs before it goes on to object-test everything.

Thursday, November 1, 2012

ASCII Errors

So, the last couple weeks, I have been distracted by the annual IF Competition. No Hugo games this year, but I decided it was a good year to make an honest effort at playing and reviewing the games. I got through them, but I tell you, playing other people's games is nowhere near as satisfying as making progress in one's own game or coding ability.

I tried to jump back into the Hugo swing of things yesterday. It went about as well as could be expected. There was a moment where I tried to remember why I thought my PrintStatusLine redesign the other week was a good idea (I mean, I still think it is, but it sure looks kind of a monstrosity at first glance). I moved on to my WIP, where I spent an ungodly amount of time trying to come up with a good, unused name for a bowling alley. I made some progress but I'm not sure I'm there yet.

I switched my attention to another part of the code where I had a random ASCII-key-printing thing. In fixing some problems, I learned a couple things. One, my ASCII "clip text" file that I made for EditPlus had some issues. For one thing, I made no notation for when it skipped numbers, which made it possible for me to forget that in such cases like my randomizer, I couldn't use every value between a and b (like my code was doing). When the official Hugo interpreter gets a non-valid number, it doesn't really print anything. Hugor, on the other hand, does one of those "hey-we-couldn't-print-this-character" characters.

So, I went back over my EditPlus clip text file and fixed the misleading entries (and added a BAD token for unaccounted values). On one hand, the lower range of ASCII values, where all of the normal letters are, have none of those troublesome gaps. Still, some fun ASCII symbols, like yen symbols and what not, are in the higher ranges. I also updated the Hugo by Example ASCII page so it now links to the best ASCII value chart I found in my limited searches.

Part of the reason I was attacking this ASCII thing was because Hugor had an unexpected gap at the end of a line that should have printed to the end. Eventually, I figured it out that just calling system(61) (which checks if the interpreter is a simple port) makes Hugor think we have progressed a character position, so commands like 'print to display.linelength;' always come up short.

Anyhow, I'm pretty happy to have found this one, as I think there's a good chance it's responsible for the weird behavior I wrote about in this post.

So, don't know if I'm entirely in Hugo mode yet, but that can't be a bad start. Anyhow, the HugoComp officially starts tomorrow (I'll make a post to intfiction.org), so it'd be really nice to wrap up one of these projects real soon like. Fingers crossed.