[DGD] SkotOS 2.0

Christopher Allen ChristopherA at skotos.net
Tue Aug 31 21:40:32 CEST 2004


Par Winzell wrote:
> It's not interpreted in LPC. Merry is compiled into actual LPC. There
> is no significant speed loss. The benefits are not so much in the
> language itself but in the fact that it allows little snippets of
> code to be sent around as data, which is a natural way for us to hook
> logic onto events.

The name Merry comes from "Mediated Execution Registry Involving A Dialect Of C" -- which although somewhat a joke because it is the successor of Bilbo "Built In Language Between Object", but the acronym that Merry stands for actually does describe it well.

The key is the the "mediated" part -- this means that Merry is somewhat sandboxed -- not to the scale that Java is, but helpful. For instance, in Merry code we try to ensure that Merry scripts inside game objects only interact with objects that involve the virtual game world, not random other objects it has no business with. Only a limited number of functions can be called from Merry, and even then they are only allowed implicitly, like fetching/setting properties, Act(), etc. File system access is completely disabled, as well as things like starting things up or shutting down processes, and some communications functions.

In addition, the langage is run through a complex parse_string() grammar, which gives us the ability to add some useful synatactic sugar additions: constants, per-thread global $variables, $foo: <expr> function-parameters, you can directly access the property database for values through ${someobject}.property:name, and a few other useful things like the ability to do inline SAM (Skotos Active Markup) with $"{oneof:one|two|three}".

Independent of the Merry language itself, there are a large variety of useful game specific functions, such as EmitTo(), EmitIn(), Set(), etc.

Here is a small code example. A simple torch, which responds (i.e. the "react-") to the signal (what might be called an event) named "react-post:light-dob". This signal is sent to a direct object (i.e. -dob) immediately after (i.e. -post) a verb is used that might turn on a light in an object (such as the verbs light, ignite, etc.):
/* Standard Flame Scripts   */   
/* react-post:light-dob     */   
/* Example by ChristopherA  */   
      
   /* If flame-on was set during the reaction, turn trait:flame on */   
   /* and let setprop-post:trait:flame handle cleaning things up.  */   
   /* Note: we do this in react-post so that things look clean     */   
   /* during normal processing of the react:light-dob signal,      */   
   /* e.g. you don't get the result "You light the flaming torch." */   
   /* when it isn't actually flaming yet.                          */   if ( Get(this,"trait:flame-on") ){       
      Set( this, "trait:flame", TRUE);       
      Set( this, "trait:flame-on", FALSE); 
      /* Now show everyone that a flame has appeared.              */      
      EmitTo ($actor, "The top of " + Describe($this, $actor, $actor) + " crackles alight.\n");     
      EmitIn(Get($actor, "base:environment"), "The top of " + Describe($this) + " crackles alight.\n", $actor); 
   }   
   
   /* This is a react-post, so no need to return TRUE; */Note the $actor, $this -- these are per-thread global variables passed to this script. They will be the same for all the signals that were initially caused by the first "light my torch" verb.

Next we have a signal that is sent to objects if a property is changed, called setprop-post. An example of use is when the torch is lit -- either someone interacted with the torch directly (for instance "light my torch" above) or something else lit it (put my torch in fireplace) -- either way, the trait:flame property is set to true and setprop-post:trait:flame is sent to the object.

/* Standard Flame Scripts    */
/* setprop-post:trait:flame  */
/* Example by ChristopherA   */

   if ($(hook-property) != "trait:flame") return TRUE;

   /* If a flame exists...   */
   if ( Get(this,"trait:flame") ){
      /* reveal the flame and change adjectives on the prime detail */
      Set( this, "details:flame:hidden", FALSE);
      Set( this, "details:smoke:hidden", FALSE);
      Set( this, "details:default:adjectives:flaming", TRUE);
      Set( this, "details:default:adjectives:lit", TRUE);
      Set( this, "details:default:adjectives:unlit", FALSE);

      /* if the torch has never been flamed before, scorch it.     */
      if ( ! (Get(this,"trait:flame:remains")) ) {
         Set(this, "trait:flame:remains", "It is slightly scorched.");
      }

      /* Now start the timer which will tick ever 90 seconds      */
      Set(this, "trait:flame:tick_id", Every("tick", 90));

   }
   /* ... if a flame does not exist, then hide the flame and      */
   /* change adjectives on the prime detail.                      */
   else {
      Set( this, "details:flame:hidden", TRUE);
      Set( this, "details:smoke:hidden", TRUE);
      Set( this, "details:default:adjectives:flaming", FALSE);
      Set( this, "details:default:adjectives:lit", FALSE);
      Set( this, "details:default:adjectives:unlit", TRUE);

      /* Now turn of the timer, so it will not tick. */
      Stop(Get(this, "trait:flame:tick_id"));
   }

   /* This is a setprop-post, so no need to return TRUE; */Finally, we need a way to emit messages periodically as the torch is burning, and to destroy the torch when it is used up:

/* Standard Flame Scripts    */ 
/* timer:tick                */ 
/* Example by ChristopherA   */ 
 
   /* timer:tick is called every X seconds (typically 60 or */ 
   /* 90 seconds) as set up in the Every() function in      */ 
   /* the setprop-post:trait:flame merry script.            */ 
 
   object env; object act; int tick; 
 
   /* We've been triggered, so increment our tick count     */ 
   tick = Get(this, "trait:flame:tick_cnt") + 1; 
   Set(this, "trait:flame:tick_cnt", tick); 
 
   /* set some variables because timer:tick exists in an    */ 
   /* an environment where there are very few arguments     */ 
   /* and you can't rely on where the item. It could be     */ 
   /* on the floor, in someones bag in the nil, or in       */ 
   /* the hands of the person who lit it.                   */ 
 
   /* If we are being held, we want our environment output  */ 
   /* to be in the room of the person who is holding us.    */

   if (env = Get(this, "base:environment")) { 
    env = Get(env, "base:environment"); 
   } 
 
   /* If we are being held, we want our actor output        */ 
   /* to be the person holding us, or the room if the item  */ 
   /* has been dropped.                                     */

   act = this."base:environment"; 
   if (act."base:environment") { 
      /* actor has an environment, all is well */ 
      env = act."base:environment"; 
   } else { 
      /* if act has no environment, we've been dropped and act = env! */ 
      env = act; 
      act = nil; 
   } 
 
   /* For each tick, do different things...                 */ 
 
   /* ...partially burn out the item                        */ 
   if (tick == 5) { 
     Set(this, "trait:flame:remains", "It is about half burned out"); 
   } 
   /* ...mostly burn out the item                           */ 
   if (tick == 10) { 
      Set(this, "trait:flame:remains", "It is almost burned out."); 
   } 
   /* ...complete burn out the item                         */ 
   if (tick >= 12) { 
      if ( act != nil ) { 
          EmitTo(act, Describe(this, nil, act) + " sputters out.\n"); 
      } 
      if(this."base:environment") { 
         EmitIn(env, Describe(this, nil, nil) + " sputters out.\n", act); 
      } 
      Set(this, "trait:flame", FALSE); 
      Set(this, "trait:flame:flammable", FALSE); 
      Set(this, "trait:flame:remains", "It is completely burned out."); 
      Set( this, "details:default:adjectives:spent", TRUE); 
      /* stop us from ticking anymore                       */ 
      Stop(Get(this, "trait:flame:tick_id")); 
 
      /* Now return false, but set up a delay so that       */ 
      /* this object can decay in 86400 seconds (1 day)     */ 
      $delay(86400, FALSE, "4a8d"); 
 
      /* We have to re-establish our local variables, as    */ 
      /* they are incorrect after a delay.                  */ 
      Set(this, "trait:flame:tick_cnt", tick); 
      if (env = Get(this, "base:environment")) { 
    env = Get(env, "base:environment"); 
      } 
      act = this."base:environment"; 
      if (act."base:environment") { 
         env = act."base:environment"; 
      } else { 
         env = act; 
         act = nil; 
      } 
 
      /* If we are not in the nil, then tell people that    */ 
      /* we are disintegrating...                           */ 
      if ( act != nil ) { 
        EmitTo(act, Describe(this, nil, act) + " disintegrates from age.\n"); 
     } 
      if (this."base:environment") { 
         EmitIn(env, Describe(this, nil, nil) + " disintegrates from age.\n", act); 
     } 
    /* Ok, we are done. Bye.                              */ 
      Slay(this); 
      return FALSE; 
   } 
 
   /* For every tick, there is a 30% chance of a random emit*/ 
   switch(random(10)) { 
      /* Again, if we are not in the nil, do the emits      */ 
    case 0: { 
            if ( act != nil ) { 
               EmitTo(act, "A trail of smoke wisps upwards through the air from " + Describe(this, nil, act) + ".\n"); 
            } 
            if(this."base:environment") { 
            EmitIn(env, "A trail of smoke wisps upwards through the air from " + Describe(this, nil, nil) + ".\n", act); 
            } 
    } break; 
    case 1: { 
            if(this."base:environment") { 
               EmitTo(act, "There is a quiet fizzle of hot oil from " + Describe(this, nil, act) + ".\n"); 
            } 
            if(this."base:environment") { 
               EmitIn(env, "There is a quiet fizzle of hot oil from " + Describe(this, nil, nil) + ".\n", act); 
            } 
    } break; 
    case 2: { 
            if(this."base:environment") { 
               EmitTo(act, capitalize(Describe(this, nil, act) + " flickers.\n")); 
            } 
            if(this."base:environment") { 
               EmitIn(env, capitalize(Describe(this, nil, nil) + " flickers.\n"), act); 
            } 
    } break;
    default: break;
   }

The above is only three of about 8 different Merry scripts used by torches.

-- Christopher Allen

------------------------------------------------------------------------
.. Christopher Allen <ChristopherA at skotos.net>       Skotos Tech Inc. ..
..                2342 Shattuck Ave Ste #512, Berkeley, CA 94704-1517 ..
.. www.skotos.net  www.rpg.net       o510/647-2760   f510/849-1717 ..

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.dworkin.nl/pipermail/dgd/attachments/20040831/49629045/attachment.html>


More information about the DGD mailing list