Bonus Movie and Mr Spock

by Ron Gilbert
Feb 27, 2015

First of all, I usually don't get choked up when celebrities die. I enjoyed their work, I will miss their new work, but I didn't know them. I feel for their families and friends, but it's not a loss I internalize.

There have been two exceptions. The first was John Candy. I don't know why his death hit me like it did. The second is Leonard Nimoy, which I found out about today on Twitter while I was compiling (no, really, I was compiling).

Star Trek is an indescribable part of my life. Spock really does feel like a friend. I loved Next Gen, but Star Trek will always be Kirk, Spock, Uhura, McCoy and Scotty. It's what I watched as a kid; everyday after school. I even went to a Star Trek convention back in '89.

This is as close as Gary and I have to something to memorialize Mr Spock. Live Long and Prosper.

Now on to the fun stuff.

Here is this week's video, complete with animationing objects.

There are three reasons I didn't get as much done as I wanted to this week:

1) I goofed around too much. Monday and Tuesday are just days I want back.

2) I decide to refactor a bunch of code having to do with how an object's images are represented in the room data files. Animated objects were being kept in separate .json files and it was getting unwieldy, so I moved animated objects into the room's .json file. They are never seen outside the room, so they can just live in there.

3) I ran into a odd Squirrel bug that took me a couple of days to figure out. It wasn't a bug IN squirrel, more of how I was using the API (many thanks to Vincent Hamm for helping and listening to me debug and gripe on IM). There are a lot of really great things about Squirrel, documentation isn't one of them. One thing I worry with Squirrel is that I'm really bending it to get it to do what I want and someday I'm going to break it.

The managing of object states is pretty verbose right now. As time goes on, I'll streamline a lot of it so less scripting code is needed.

Here is the code:

Bank <-
{
    background = "Bank"

    enter = function()
    {
        if (bankClock.state == OPEN) {
             playAnimation(bankClock, "state_open")
        } else {
            playAnimation(bankClock, "state_closed")
        }
    }

    exit = function()
    {
    }

    bankPainting =
    {
        look_count = 0
        name = "Cheap Painting"
        verbLookAt = function()
        {
            if (look_count == 0) { currentActor.say("You'd think a bank could afford a better painting.") }
            if (look_count == 1) { currentActor.say("This looks like a knockoff.") }
            if (look_count == 2) { currentActor.say("I think I can see the numbers behind the paint.") }
            if (look_count >= 3) { currentActor.say("I'm done looking at this painting.") }
            look_count++
        }
    }

    bankClock =
    {
        name = "Clock"
        state = CLOSED

        verbLookAt = function()
        {
            if (state == CLOSED) {
                currentActor.say("It's who cares'o clock.")
            } else {
                currentActor.say("It's gears all the way down.")
            }
        }

        verbOpen = function()
        {
            if (state == OPEN) {
                currentActor.say("That is already open.")
            } else {
                state = OPEN
                playAnimation(bankClock, "do_open")
            }
        }

        verbClose = function()
        {
            if (state == CLOSED) {
                currentActor.say("That is already closed.")
            } else {
                state = CLOSED
                playAnimation(bankClock, "do_close")
            }
        }
    }

    bankToMainStreetDoor =
    {
        name = "Door"
        verbWalkTo = function()
        {
            print("here")
            enterRoomFromDoor(TestStreet.TestStreetToBankDoor)
        }
    }
}

name: "Bank"
sheet: "BankSheet"
background: "bank"
objects: {
    bankPainting: {
        usepos: "{83,26}"
        usedir: 8
        hotspot: "{{40,120},{119,82}}"
    }
    bankClock: {
        animations: [
            {
                name: "do_open"
                frames: ["bank_clock1""bank_clock2""bank_clock3"]
            }
            {
                name: "do_close"
                frames: ["bank_clock3""bank_clock2""bank_clock1"]
            }
            {
                name: "state_closed"
                frames: ["bank_clock1"]
            }
            {
                name: "state_open"
                frames: ["bank_clock3"]
            }
        ]
        pos: "{158,101}"
        zsort: 158
        usepos: "{161,26}"
        usedir: 8
        hotspot: "{{146,114},{171,88}}"
    }
    bankToMainStreetDoor: {
        usepos: "{17,11}"
        usedir: 2
        hotspot: "{{0,125},{12,0}}"
    }
}
props: {
    bankRope: {
        image: "bank_rope"
        pos: "{300,32}"
        zsort: 6
    }
}

- Ron



Vegetaman - Feb 27, 2015 at 17:35
Refactoring code is always a good feeling. And I do not envy the position of having to work with a third party library/API with a lack of documentation on its workings (though what I like just as much is a bunch of best practices examples of how you are supposed to use it in a real system, not a demo system that doesn't represent what any production project would ever look like). I have been burned a great many times and when finally getting technical support being told "well, you're not supposed to use it LIKE THAT!".

I had a nice chuckle at "it's who cares o'clock", haha.

Also: LLAP. \\//_

gaston - Feb 27, 2015 at 17:50
I would love to know how(i'm learning game dev basics, dont laugh)
- you manage the character to be depending on its position in front or behind the ropes
- how you handle when you have animations of two different objects interacting (may be it's not there in the video, but will happen)
- what the zsort is
- what use dir is

love the blog

Ron Gilbert - Feb 27, 2015 at 17:58
zsort is the answer to your first question. It's the render order and as actors move around, they get sorted accordingly. It's basically the ypos. usedir is the direction an actor faces the object when the use it.

Peter Brodersen - Feb 28, 2015 at 01:25
As a random exercise I was trying to guess what the use did value refers to. 8 seems like a high number for four directions. At first I was thinking diagonals (with up-right as 1 and then going clockwise ending at 8 for up) but 2 for exiting the bank confused me (unless the direction was counter-clockwise). My next guess is some kind of bitmask possibility with 8 as bit 4. Is there more to it? I know this is just random speculations on a very arbitrary and perhaps irrrlevant point but nothing is too small to delve at for fan geeks with too much spare time on our hands.

As a side note I do actually like the current usage of structures of verbLookAt. Even though it might be simplified (or "optimized") with simple lists for currentActor.say() some of the cool stuff in the adventure games was when other stuff happened when you were looking at something. For instance your character were looking at an item in the shop, saying out loud his observation and the shop owner would chime in with further informationat this current setup it would be easy to add further events or actions when somebody is looking at something. My favourite was Bernard in Maniac Mansion reading the security door warning and then trying to crackpot the code by himself.

These opportunities make for a more "realistic" approach where Looked isn't simply a suspension of disbelief of why the character actually has to say stuff out loud for the person playing the game to read. Every time I looked at an item in the old adventure games and other characters in the scene/room reacted I was positively surprised and felt more immersed in the game.

Ron Gilbert - Feb 28, 2015 at 11:00
From the engine:

const int kDirNone = 0x00000000;
const int kDirRight = 0x00000001;
const int kDirLeft = 0x00000002;
const int kDirFront = 0x00000004;
const int kDirBack = 0x00000008;

At some point, I'll replace the numbers in the .json file with symbols so I can changes them in the engine code and not have to go into 100 .json files and change the values.

Vegetaman - Feb 27, 2015 at 18:05
Question for you Ron... Are you building and running this as a completed "file" (where you have to compile the whole project and then install the demo and run it), or are you able to live run these demos on the fly when you code them up in some sort of debug mode or pipe the output to some simple viewer program? And further still, do you have real time debugging capability when doing this? I imagine the latter approaches would be quite handy for this kind of work (but I know that such real time debug environments aren't always available). Just curious.

Ron Gilbert - Feb 28, 2015 at 11:06
We can change art and scripts (the Squirrel code) without recompiling the engine. We just make the change and run the game again. At some point I might add the hot-loading of scripts, so we can make changes and not have to even re-run the game.

Leando - Feb 27, 2015 at 23:13
Verbs functions like verbLookAt could greatly benefit from currying. You will still be invokating verbLookAt , but code will be much more scalable and decoupled.

Ron Gilbert - Feb 28, 2015 at 11:09
Can you provide an example? I'm not sure I want the code too decoupled. At least for an adventure game scripting language, I tend to favor clarity. But I'm curious as to your idea.

Leandro - Mar 02, 2015 at 22:27
I am not very familiar with Squirrel but it should look something like this

lookAtStrings = {
"You'd think a bank could afford a better painting."
, "This looks like a knockoff."
, "I think I can see the numbers behind the paint."
, "I'm done looking at this painting." }



buildVerbLookAt = function( responseStrings , actor )
{
    cycleIndex = 0

    buildVerb = function()
    {
actor.say( responseStrings [cycleIndex])
if ( cycleIndex = <  ( responseStrings.len() - 1) )
  { cycleIndex++}
    }

    return buildVerb
}

verbLookAt = buildVerbLookAt( lookAtStrings , currentActor )

Since verbLookAt at is build via currying , it's completly decoupled from response strings and a actor , that way it is possible to "mix and match " different response strings with different actors , besides , once created , verbLookAt automatically manages response without need for a clumsy if branching.

Derrick Reisdorf - Feb 28, 2015 at 01:05
Wouldn't the first frames of do_close and do_open be redundant (bank_clock_1 and bank_clock_3, respectively)?

Zombocast - Feb 28, 2015 at 01:08
I was watching the "Object Animation" video, and fell in-love the auto-pan feature when you stepped outside*.
I'm also seeing allot of "quality of life" features like "hold to move" in-staid of "point and click".. click......click*
This is refreshing to see in such early development, "2.D sprites with a touch of modern tech" Nostalgia overload.
One thing that caught my eye was how well you captured the speed and fluid movement of the models *feels like 60fps.

You are truly adding a gem to the 1987-1990 era of PC gaming that has been lost in time.
Unfortunately my heart lies in the 1993-1995 years of Day of the Tentacle and Full Throttle. When Lucas Arts
and Sierra really pushed the more intricate MS Paint style Sprites and animation that peaked just before 3.D models.
But that's more personal taste than critique, like comparing 1964 vs 1972 mustang body styles.

Jim - Feb 28, 2015 at 01:59
What happens if you check the painting enough number of times and look_count gets overflowed? :)

Dominik - Feb 28, 2015 at 02:41
Well, lets say it's stored as a 32 bit integer with 1 bit used for plus/minus. And lets assume one could perform a look each second, then one would need about 70 years to overflow the variable. Have fun ;-)

Dominik - Feb 28, 2015 at 02:46
Thanks for the update - also your words about Mr. Nimoys passing made my wife cry again. She is a giant fangirl of him and his work.

Why is the initialization for the clock inside the enter method of the bank and not within an "onEnterRoom" method of the clock object?

Steffen - Feb 28, 2015 at 04:57
Is it just me or do characters walking directly towards or away from the players perspective look stupid with their wildly shaking arms? And I don't mean that in a funny way. Somehow characters also seem to move twice as fast when going in that direction. Maybe that is the problem?
Having played Maniac Mansion just recently (as recent as "last week") what I want to say is that it just doesn't "feel" right. Oh, speaking about the right (look) and feel, the rest is just perfect! I totally love it! :-D

@Ron: Could you maybe make a video with characters moving at "half speed" some day, please?

Steffen - Feb 28, 2015 at 05:04
I just had a look at "We're Walking And We're Walking". It doesn't seem to be better in half speed. I think the movement of the arms just is too obtrusive. Just my $0.02 though... :-)

Ron Gilbert - Feb 28, 2015 at 10:57
I think the issue is you're nit-picking way to early in the project. My goal is to get the systems up and running with lots of knobs to tune.

Over the next few months, you're gong to see a lot of crappy and unfinished art go into the game. Games aren't built by polishing one thing until it's right then moving to the next. I get things in, make sure the code is flexible, then move on the next. There will be a point where the whole game looks like a giant piece of garbage. That's totally normal, then we'll go though and fix everything in multiple passes, each time making it a little better. We do this to better hit a dead line.  If we're polishing in layers, we can stop at some point and know the whole game exists at the same level of quality. It's how I've always made games and the way most people do.

Hopefully this blog is a glimpse into that process. I love getting all the comments, but ofter the response is "yeah, I know".

Steffen - Mar 03, 2015 at 06:45
Thanks for your answer, Ron!

Sorry for being nit-picky. Please take it as a compliment. "Wow, everything looks so great, almost perfect already! Just that one little aspect though, how could they let that slip?" Sounds better? Because that"s what I was trying to say. I'm really amazed by the progress so far that I'm thinking  "that looks almost done, what could they possibly be doing another 12 months?" from time to time.

Gee, I think I'm bad at giving compliments...

Ron Gilbert - Feb 28, 2015 at 11:04
Yes, I play all the way through Maniac Mansion last month and when I was doing to walking code, I looked at how the characters walked and matched their speed. I also went into the original SCUMM engine source code to see how we did the "fake perspective".

All that said, this is just a first pass. I can now tune all the variables I need so I'm on to the next task. As Gary does more animation we'll tune and adjust. The animation of her walking was a first pass. Nothing is final at this point.

Dominik - Feb 28, 2015 at 07:46
(Warning: long post - I'm sorry...)

I have thought about the verbosity in this sample script and want to add my 2 cents to this:

Why not give each actor a general purpose state flag (32bit Int or whatever). The states can be represtend by bits of the flag (and therefore constants) which means you would check a state like this:

if ( state & OPEN ) {...}
if ( state & CLOSED ) {...}

To make state handling even easier I would add some nice helper methods:

sateIs( toCheck): Returns the current object if the state flag is set, otherwise returns dummy actor (see my comment in "UI Action").

sateSwitchTo( toSet ): If the state is already set, return the dummy actor, otherwise set the state flag and return the current object

stateElse(): Returns the current object if no "stateSwitchTo" or "stateIs" has succeeded since calling the last event handler (i.e. "onVerb"), otherwise returns the dummy actor

Also in the follwing sample "animationPlay" is a mehtod of the actor class and the say-method
was tweaked so that it performs the following check:

function say( text, [...] ) {
if ( this == actorDummy ) return(actorDummy)

if ( !this.isPlayable ) {
return( currentActor.say( text, [...] ) )
}

[...]
}

This way "say" can used by used on the object but will execute on the actorCurrent. This makes scripting with daisy-chaning methods easier.

Also I moved the intial animation from the "enter" method of the room to an "init" method of the object


Bank <-
{
background = "Bank"

bankPainting =
{
name = "Cheap Painting"

onLookAt = function()
{
actorCurrent.say("You'd think a bank could afford a better painting.",
"This looks like a knockoff.",
"I think I can see the numbers behind the paint.",
I'm done looking at this painting.")
}

onPickUp = function()
{
who(Ransome).doThis(function() {
say("Sure, why not....")
pickUp(bankPainting)
delay(500)
facePlayer()
say("I'm now the proud owner of this painting.")
delay(100)
say("What the hell am I going to do with this?")
delay(100)
say("NO, I'm not carrying this thing around all day!")
faceObject(bankPainting)
putBack(bankPainting)
})

whoElse.say("I don't want that stupid painting")
}

}

bankClock =
{
name = "CLOCK"

// State constants
CLOSED = 1
OPEN = 0

init = function()
{
stateIs(OPEN).animationPlay("open");
stateIs(CLOSED).animationPlay("closed");
}

onLookAt = function()
{
stateIs(CLOSED).say("It's who cares o'clock")
stateIs(OPEN).say("It's gears all the way down")
}

onOpen = function()
{
stateSwitchTo(OPEN).animationPlay("do_open")
stateElse().say("That is already open.")
}

onClose = function()
{
stateSwitchTo(CLOSED).animationPlay("do_close").say("Great, now what?")
stateElse().say("It doesn't get more closed than this.");
}

}
}

Sorry for the long post but reading your blog really get my gears going! It's very interesting to read and a nice brain teaser to think about how one I would approach this.

Ron Gilbert - Feb 28, 2015 at 11:13
Your idea is having an "init" or "enter" script for each object is interesting and pretty easy to do. I have liked there being a main "enter" script for the room because as the game gets more complex, you're setting up a lot of stuff based on game state and it's useful to have that all in the place, rather then looking through each object.  But I can see benefits if the "enter" code is very object specific. I might add it and see how it goes.

Mattias Cedervall - Feb 28, 2015 at 13:08
I'm a trekkie and I'm very sad to hear about Leonard Nimoy's passing. :-( He will be missed! I wish I could've met him. I've met Marina Sirtis here in Sweden back in 2005 so I'm glad I've at least met one of the Star Trek actors. May they all live long and prosper!

Ron, maybe you could do them a favor and document Squirrel?

PrinzJohnny99 - Feb 28, 2015 at 13:24
Having absolutely no clue of programming and with the feeling that these posts with them black screenshots with coloured writing could also be written in chinese and I wouldn't understand one word less, I think you should have used a two-headed-squirrel.
I hope this post isn't stupid.

Peter Campbell - Feb 28, 2015 at 20:45
I think everyone was hit pretty hard when John Candy suddenly died.  He wasn't just a great comedian and a great actor but perhaps the nicest, sweetest guy to have ever lived?  It's hard to argue against that.  

He played so many loveable characters on screen, such as the dad from Summer Rental, the security guard from Vacation, Del from Planes, Trains & Automobiles (my favorite performance of his, and wow was he great at being a dramatic actor especially when giving his character's sad confession at the end), the Polka King who offers Kevin's mom a ride back to Chicago in Home Alone, etc. etc.  All of these super nice, super funny characters that he played, by all accounts everyone who knew him and worked with him said that off-screen in real life, he was just as nice and wonderful of a guy.  Any time he appeared on screen, even if it was a bad movie, he instantly made the movie 100x more enjoyable to watch because he was just that incredibly likeable.  

John Candy, Chris Farley, John Pinette, Mitch Hedberg, Patrice O'Neal, there's just way too many super nice comedians who've died way too young who's deaths could've been prevented if they just took care of their health a little bit better.  Easier said than done I know, the entertainment business puts an intense amount of pressure on those in it to stay relevant which often leads to maintaining health as being at the bottom of one's priority list.

Mattias Cedervall - Feb 28, 2015 at 21:25
I also miss John Candy.

Gianni - Mar 02, 2015 at 11:34
Nice progress, Ron!
I'd have a question regarding the verb interface: is it implemented in the engine code or in Squirrel code?
I ask this because I remember you showed a clickedAt(x,y) Squirrel function: being the code in Squirrel would allow you to define an override to the interfaces e.g. if the player needs to do some sort of click puzzle.
Is that the case?
Keep up the great job!

2600hz - Mar 02, 2015 at 20:15
Is that payphone a cocot, and can we phreak it?

Demetris Thoupis - Mar 03, 2015 at 13:47
Hi Ron,
    One small comment on these test videos is that when the actor stops in front of the object there should be a very small delay before the subtitles come up. Of course is the newer processing which makes everything faster but that is my opinion. Not having also the animated talking heads makes the whole subtitle thing a bit odd as well. Just my opinion.
Demetris

Alex C - Mar 09, 2015 at 12:05
Looking good. One of the things I hope will come during the progressive refinement process is giving each of the currentActor's their own voice. Rather than having them all say "That is already open", let one of them use a contraction, one of them say "Um, duh, it's already open!", and so on. Tweaks like that obviously help it feel like there's actually a different character walking around the world, not just different thin faces over the same "generic player character".

Bucky - Mar 09, 2015 at 19:02
"I was compiling..."
I love the classics, the artist's equivalent is
"I was rendering..."
Although the artist's sounds like it could have more sinister connotation and involve beef tallow or quick lime.