Setting an attribute from an ASLEvent

K.V.

I want to set attributes from JS.

This works perfectly, but does anyone see (or know of) a better approach than this?

  <function name="SetFromAsl" parameters="paramToSplit"><![CDATA[
    params = Split(paramToSplit, ";")
    if (not ListCount(params) = 3) {
      error ("Syntax error.")
    }
    obj = params[0]
    object = GetObject(obj)
    att = params[1]
    value = params[2]
    if (value = "true") {
      value = true
    }
    else if (value = "false") {
      value = false
    }
    else if (IsInt(value)) {
      value = ToInt (value)
    }
    else {
      if (StartsWith(value, "object")) {
        value = Split(value, ":")[1]
        foreach (o, AllObjects()) {
          if (o.name = value) {
            value = o
          }
        }
      }
      else if (StartsWith(value, "script")) {
        msg ("<br/>Error: You can't set a script this way.<br/>")
        return (true)
      }
    }
    set (object, att, value)
    msg (object.name+"."+att+" = "+value)
    msg ("Done.<br/>")
  ]]></function>

My start script to test it out:

SetFromAsl ("player;froody;true")
SetFromAsl ("player;parent;object:second room")
SetFromAsl ("thingy;pull;script:msg(\"You pull it.\")")
SetFromAsl ("player;insecondroomcount;0")

Revised.

Thanks Pixie and mrangel!


Minor tweaks, but I would set value to an int instead of creating the new variable valueAsInt, then you last if/else is not required, just set to the value.

You could also set it to split on the first full stop and equals, rather than semi-colons, so it would accept "player.money=49" ...


For the object part, it seems rather inefficient to loop over all objects, and then call GetObject anyway. You could skip one of those steps. Either:

foreach (o, AllObjects()) {
  if (o.name = value) {
    value = o
  }
}

or:

o = GetObject(value)
if (not o = null) {
  value = o
}

But I'd be a little nervous about that; can you be sure you'll never want to set an attribute to a string that is also the name of an object? If I was writing this, I'd probably have some initial character. Maybe 'value' starts with a @ or something, to indicate that it's an object name.

I posted code fairly recently that does pretty much the same thing, setting variables from a string. I'll see if I can find it.


(edit. Silly error)

My 'HandleCharacterCreationResult' function, trimmed down so it could be used in other circumstances. Single parameter, 'result':

pattern = "^\\s*((?<object>[^-=+.]+)\\s*\\.)?(?<attribute>.+?)=?(?<operator>[-=+])\\s*(?<value>\\S.*?)\\s*$"
if (IsDefined("result")) {
  if (TypeOf(result) = "string") {
    foreach (part, Split(result, ";")) {
      if (StartsWith(part, ":")) {
        msg (Right(part, LengthOf(part)-1))
      }
      else if (not StartsWith(part, "#")) {
        error ("I don't know what to do with: "+result)
      }
      else if (IsRegexMatch(pattern, part, "hairyregex")) {
        result_parts = Populate(pattern, part, "hairyregex")
        resultobject = game.pov
        if (DictionaryContains(result_parts, "object")) {
          objectname = StringDictionaryItem(result_parts, "object")
          if (LengthOf(objectname) > 0) {
            resultobject = GetObject(objectname)
            if (resultobject = null) {
              error (DictionaryItem(result_parts, "object")+" is not an object")
            }
          }
        }
        oldvalue = ""
        value = DictionaryItem(result_parts, "value")
        if (StartsWith(value, "'")) {
          value = Right (value, LengthOf(value)-1)
        }
        else {
          if (StartsWith(value, "{")) {
              value = ProcessText(value)
              if (StartsWith(value, "{")) {
                  value = Right (value, LengthOf(value)-1)
                }
                if (EndsWith(value, "}")) {
                value = Left (value, LengthOf(value)-1)
              }
            }
            if (StartsWith(value, "'")) {
              value = Right (value, LengthOf(value)-1)
            }
            else if (LCase(value) = "true") {
              value = true
            }
            else if (LCase(value) = "false") {
              value = false
            }
            else if (IsInt(value)) {
              value = ToInt(value)
              oldvalue = 0
            }
          }
          else if (StartsWith (value, "@")) {
            assign = Split(Right(value, LengthOf(value)-1), ".")
            oname = assign[0]
            forceobject = false
            while (StartsWith(oname, "@")) {
              forceobject = true
              oname = Right (oname, LengthOf(oname)-1)
            }
            list remove (assign, oname)
            GetObject(Trim(oname))
            if (o = null) {
              if (HasAttribute (resultobject, oname)) {
                o = GetAttribute (resultobject, oname)
              }
            }
            foreach (element, assign) {
              if (not o = null) {
                if (TypeOf(o) = "object") {
                  o = GetAttribute(o, element)
                }
              }
            }
            if (forceobject or not o = null) {
              value = o
            }
          }
          attrname = StringDictionaryItem(result_parts, "attribute")
          if (HasAttribute(resultobject, attrname)) {
            oldvalue = GetAttribute(resultobject, attrname)
          }
          switch (DictionaryItem(result_parts, "operator")) {
            case ("+") {
              value = oldvalue + value
            }
            case ("-") {
              value = oldvalue - value
            }
          }
          set (resultobject, attrname, value)
        }
      }
    }
  }
}

result can be a semicolon-separated list of instructions, each of which is one of:

  • :Some string to print (I could have removed this, but figured there might be some use for it in debugging. As it's printed, you could use this if you want to print a JS string containing text processor directives)
  • # (does nothing; could have removed this but I figured there might be some use for it)
  • object.attribute=value
  • object.attr1=@attr2
  • object.attr=@objectname
  • [email protected]
  • object.attr=@@object2.attribute2.attribute3 - If one of the attributes doesn't exist, this version sets null . The single-@ version sets it to the string.
  • object.attr={random:foo:bar:baz}
  • object.attr='{this has brackets in it, but a single quote to tell it not to run the text processor}
  • object.attribute+value and object.attribute-value - add a value to or subtract it from an existing attribute; use with strings, lists, or ints, but it will probably error if used on a type that doesn't have a + or - operator

(in all these cases, the object is assumed to be the player if not specified)


K.V.

@Pix

> I would set value to an int instead of creating the new variable valueAsInt

I had it that way in the beginning.

At first, I was doing this:

SetFromAsl (player;insecondroomcount;0)

If you don't use the quotes, the integer is passed as a string. Then, it will not convert that 'integer as a string' to an integer inside of that function.

At one point (before I figured out to use the quotation marks when calling it without JS), I had this:

else if (IsInt(value)) {
      value = ToInt (value)
      valueAsInt = ToInt(value)
     set(object, att, ToInt(value))
  }

...and guess what?

It still set it up as a string: "0".

Truthfully, I don't know what I had going on, and I wish I'd have saved a backup of that crazy scipt!


Now, I've been playing with what I've got now (which is what is currently in the first post in this thread), and I was initially calling SetFromAsl without using JS.

That worked perfectly fine.

So, I switched it up to this:

    <start type="script">
      JS.eval ("ASLEvent('SetFromAsl', 'player;froody;true');")
      JS.eval ("ASLEvent('SetFromAsl', 'player;insecondroomcount;0');ASLEvent('SetFromAsl', 'player;parent;object:secondroom');")
      JS.eval ("ASLEvent('SetFromAsl', 'thingy;pull;script:msg(\"You pull it.\")');")
    </start>

    <enter type="script">
      JS.ASLEvent("SetFromAsl", "thingy;look;It is blue.")
    </enter>

...and it finishes a turn each time an ASLEvent is called. (Hehehe. I knew something had to be off because the script appeared to work after only two tweaks.)

If I had been thinking, I'd have just used the script from Pixie's CHEAT commands to set things up with object.attribute=value, like he suggested here...

...but I'd still be cursing the ASLEvent for finishing the turn.

function ASLEvent(event, parameter) {
    UIEvent("ASLEvent", event + ";" + parameter);
}

function UIEvent(cmd, parameter) {
    questCefInterop.uiEvent(cmd, parameter);
}


PS C:\Program Files (x86)\Quest 5> grep -r questCefInterop *
desktopplayer.js:    questCefInterop.uiEvent(cmd, parameter);
PS C:\Program Files (x86)\Quest 5>

I'm looking at your code next, mrangel. (My mind is moving slowly today. It's an 'only think about one thing at a time' day.)


You could set the value by using Eval:

if (not ListCount(params) = 3) {
  error ("Syntax error.")
}
obj = params[0]
object = GetObject(obj)
att = params[1]
value = params[2]
set (object, att, Eval(value))
msg (object.name+"."+att+" = "+value)
msg (TypeOf(object, att))
msg ("Done.<br/>")

You would have to put your strings in double quotes, but that would then differentiate from objects.


K.V.

You would have to put your strings in double quotes

Like this?

JS.ASLEvent("SetFromAsl", "thingy;look;\"It is blue.\"")

K.V.
params = Split(params, ";")
if (not ListCount(params) = 3) {
  error ("Syntax error.")
}
obj = params[0]
//If...Else added by KV
if (obj = "game.pov") {
  object = game.pov
}
else {
  object = GetObject(obj)
}
att = params[1]
value = params[2]
set (object, att, Eval(value))
msg (object.name+"."+att+" = "+value)
msg (TypeOf(object, att))
msg ("Done.<br/>")
JS.ASLEvent ("AslSet", "player;froody_dude;\"Yes you are.\"")
JS.ASLEvent ("AslSet", "player;look;\"You look down at yourself.  You see nothing abnormal.\"")
JS.ASLEvent ("AslSet", "game.pov;froody;true")
JS.ASLEvent ("AslSet", "player;parent;room2")
JS.ASLEvent ("AslSet", "player;damage_multiplier;9")
    <turnscript name="turns">
      <enabled />
      <script>
        msg ("One turn!")
      </script>
    </turnscript>
  </game>

player.froody_dude = "Yes you are."
string
Done.

One turn!
player.look = "You look down at yourself. You see nothing abnormal."
string
Done.

One turn!
player.froody = true
boolean
Done.

One turn!

You are in a room2.
player.parent = room2
object
Done.

One turn!
player.damage_multiplier = 9
int
Done.

One turn!


K.V.

It's ASLEvent that's cranking up the turn count. Definitely.

Here it is just running the new function:

AslSet("player;froody_dude;\"Yes you are.\"")
AslSet("player;look;\"You look down at yourself.  You see nothing abnormal.\"")
AslSet("game.pov;froody;true")
AslSet("player;parent;room2")
AslSet("player;damage_multiplier;9")


You are in a room.
player.froody_dude = "Yes you are."
string
Done.

player.look = "You look down at yourself. You see nothing abnormal."
string
Done.

player.froody = true
boolean
Done.

You are in a room2.
player.parent = room2
object
Done.

player.damage_multiplier = 9
int
Done.

> jump
You jump, but nothing happens.
One turn!


That code works very well, though!

I just need to figure out how chromium is telling the game to finish the turn, or not use JS to set attributes.


That's interesting.

I guess if you were doing that, you'd have to make your turnscript check if each turn was a response to a real command. (Thinking of which, is there any documentation on how the equality operator works with lists/dictionaries? Is it like Java?)


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums