Some Technical updates for this week. I have a desktop application now fully up and running. The program works as a local webserver on a computer. Starting the program activates the web server, and opens a connection to the local web service. You enter your commands via a web form, and I've managed to get the layout to mimic (more or less) an old Infocom style of Text Adventure.

I have completed the "stage", the messaging backend for objects. I've also worked out a way for the player's avatar to negotiate with the objects in the system what sort of interactions they support and also crafted an interaction framework to allow player actions to be translated into machine instructions.

All output is currently geared to output directly to HTML, with some negotiation on the server side to support complex inputs. For instance, providing a hotlink on the screen to perform an action in the game. We could also support html5 canvases and webgl at some point in the future.

The messaging system now supports two types of communication. One is a simple event, which is thrown into the system for others to make of it what they will. The second is an interaction, in which a specific actor elicits a specific response from another object. While events are easy to model as database records, Interactions were a lot harder.

I also took the time out to build everything out in a thread safe manner, and with the knowledge that some objects may be sleeping and require waking up. A sleeping object exists passively as database records. An "awake" object is one which is actually implemented as a TclOO object inside of an interpreter.

Sean being Sean, there is also a language extension to Clay to facilitate producing these interactions. Let's take a simple one "LOOK". LOOK is when an actor wants to casually glance at another object and see what they can see:

INTERACT LOOK {
  dict set reply code ok
  dict set reply object [my uuid]
  set content {}
  set name [my state get name]
  if {$name eq {}} {
    set name [my uuid]
  }
  putb content "<h3><a href=/look/[my uuid]>$name</a></h3>"
  set description [my state get description]
  if {$description ne {}} {
    putb content $description
  }
  dict set reply content $content
  return $reply
}

This interaction builds a dictionary with two important fields:

code Either ok or err
content Output to send to the browser.

This interaction assumes it is called in response to an event in the stage.event table. That event will have a to, a from, and subject, and content. The content is free-form. What I've elected to do is allow the web server to send the content of either a form or a REQUEST_PATH query as the contents of a message. For LOOK, we don't care because you get the same answer every time. But suppose we decide to model a chest

  clay::define ::stage::prop.container {
    superclass ::stage::prop
    clay set english pronoun container

    Dict state {
      locked 0
    }

    INTERACT LOOK {
      dict set reply code ok
      dict set reply object [my uuid]
      set content {}
      set name [my state get name]
      if {$name eq {}} {
        set name [my uuid]
      }
      putb content "<h3><a href=/look/[my uuid]>$name</a></h3>"
      set description [my state get description]
      if {$description ne {}} {
        putb content $description
      }
      if {[my state get locked]} {
        putb content "<p>This item appears to be locked"
      }
      dict set reply content $content
      return $reply
    }
  }

Here we have modified our LOOK interaction to take into account of the chest is in the locked state. We provide a separate interaction to UNLOCK the chest. Note that the functions are accessing the msginfo variable. This is a dictionary that contains the incoming message, and allows the function to access data from that message. In this case we need to know the actor so we can do an inventory check and an ability check.

  INTERACT UNLOCK {
    dict set reply code ok
    dict set reply object [my uuid]
    set content {}
    set name [my state get name]
    if {$name eq {}} {
      set name [my uuid]
    }
    putb content "<h3><a href=/look/[my uuid]>$name</a></h3>"
    if {[my state get locked]==0} {
      ###
      # If the chest is not locked make the player feel silly
      ###
      dict set reply code err
      putb content "<p>This item is already unlocked"
      dict set reply content $content
      return $reply
    }
    if {[my state get keycode] ne {}} {
      set keycode [my state get keycode]
      set actor [dict get $msginfo msg_from]
      ###
      # Look through the actor's inventory for a device with a matching
      # keycode
      ####
      set itemlist [my <stage> object contents $actor]
      foreach $item $itemlist {
        set obj [my <stage> wake $item]
        if {[$obj state get keycode] eq $keycode} {
          my state set locked 0
          set name [$obj state get name]
          if {$name eq {}} {
            set name $item
          }
          putb content "<p>You use <a href=/look/$item>$name</a> to unlock this [my english pronoun]"

          dict set reply time    10
          dict set reply content $content
          return $reply
        }
      }
      putb content "<p>You will need a key of type $keycode to open this [my english pronoun]"
      dict set reply code err
      dict set reply content $content
      return $reply
    }
    ###
    # If there is no specific key, allow the player to
    # pick the lock. The success is determined by their
    # lockpicking ability, calculated elsewhere
    ###
    set actor [dict get $msginfo msg_from]
    set actorobj [my <state> wake $actor]
    ###
    # Cause this action to advance the game's time by 300 seconds
    # (5 minutes, or 50 combat rounds in D&D).
    ###
    dict set reply time 300
    ###
    # Tries an action and returns either a positive or zero for
    # success (with a positive awarding experience) or a negative
    # for failure.
    ###
    set experience [$actorobj ability lockpick [my state get difficulty]]
    if {$experience >= 0} {
      my state set locked 0
      putb content "<p>You successfully picked the lock"
      putb content [my Contents_Html]
      ###
      # Award experience points
      ###
      dict set reply experience $experience
    } else {
      ###
      # Note that failure isn't a syntax error. It's a story element
      ###
      putb content "<p>You fumble a bit with the lock, but are unsuccessful in opening it."
    }
    dict set reply content $content
    return $reply
  }