Taking n items / Dropping n items

I'm getting close here.

In fact, you can DROP n ITEMS or GET n ITEMS (and it usually works).

Now, I'm working on the displayed list of objects in the room, but I just started on that, so it doesn't work right now (and may never work, I dunno).

Here's what I have so far.

NOTE: It's loaded with debugging messages.

EDITED 12/30/17 10:22 AM CST

<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Chips">
    <gameid>f2806bf4-e541-440d-8778-ea1dfdef45a7</gameid>
    <version>0.0.2</version>
    <firstpublished>2017</firstpublished>
    <showmoney />
    <start type="script">
    </start>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <enter type="script">
    </enter>
    <beforeenter type="script"><![CDATA[
      i = 4
      while (i>0) {
        CloneObjectAndMoveHere (chip)
        i = i - 1
      }
    ]]></beforeenter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <object name="poker_chips">
        <inherit name="editor_object" />
        <visible type="boolean">false</visible>
        <alias>poker chip</alias>
        <look><![CDATA[You have {poker_chips.count} chip{if poker_chips.count<>1:s}.]]></look>
        <count type="int">0</count>
        <usedefaultprefix type="boolean">false</usedefaultprefix>
        <single type="object">chip</single>
        <drop type="script"><![CDATA[
          amount = poker_chips.count
          while (poker_chips.count>0) {
            i = ToString(poker_chips.count)
            i = ToInt(i)
            left = i - amount
            // msg ("To drop: "+amount+" out of "+i+", leaving "+left+".")
            // msg (object.single)
            while (i>left) {
              newObj = CloneObject (poker_chips.single)
              MoveObjectHere (newObj)
              newObj.prototype = poker_chips.single
              i = i - 1
            }
            poker_chips.count = poker_chips.count - amount
            msg ("Done.")
            switch (poker_chips.count) {
              case (1) {
                poker_chips.alias = poker_chips.single.alias
              }
              case (0) {
                MakeObjectInvisible (poker_chips)
              }
              default {
                poker_chips.alias = ToWords(poker_chips.count)+" "+GetDisplayAlias(object.single)+"s"
              }
            }
          }
        ]]></drop>
      </object>
    </object>
    <object name="baton">
      <inherit name="editor_object" />
      <take />
      <look>This is only here to see if you can drop it.</look>
    </object>
  </object>
  <object name="chip">
    <inherit name="editor_object" />
    <alias>poker chip</alias>
    <look>A red poker chip, worth $1.</look>
    <take type="script"><![CDATA[
      if (not poker_chips.visible) {
        MakeObjectVisible (poker_chips)
      }
      poker_chips.count = poker_chips.count + 1
      if (poker_chips.count>1) {
        poker_chips.alias = ToWords(poker_chips.count)+""+" poker chips"
      }
      RemoveObject (this)
      msg ("You pick it up.")
    ]]></take>
    <multiple />
  </object>
  <command name="drop_n_objects">
    <pattern type="string"><![CDATA[^drop (?<text>\d+) (?<object>.*)$]]></pattern>
    <script><![CDATA[
      // msg (object)
      // msg (text)
      amount = ToInt(text)
      if (HasAttribute(object,"count")) {
        if (amount<=object.count) {
          i = ToString(object.count)
          i = ToInt(i)
          left = i - amount
          // msg ("To drop: "+amount+" out of "+i+", leaving "+left+".")
          // msg (object.single)
          while (i>left) {
            newObj = CloneObject (object.single)
            AddToInventory (newObj)
            DoDrop (newObj, multiple)
            newObj.prototype = object.single
            i = i - 1
          }
          object.count = object.count - amount
          msg ("Done.")
          switch (object.count) {
            case (1) {
              object.alias = object.single.alias
            }
            case (0) {
              msg ("That was your last "+GetDisplayAlias(object)+".")
              MakeObjectInvisible (object)
            }
            default {
              object.alias = ToWords(object.count)+" "+GetDisplayAlias(object.single)+"s"
            }
          }
        }
        else if (ToInt(text)>=object.count) {
          msg ("You don't have that many.")
        }
      }
      else {
        foreach (obj, object) {
          DoDrop (obj, multiple)
        }
      }
    ]]></script>
  </command>
  <command name="take_n_objects">
    <pattern type="string"><![CDATA[^get (?<text1>\d+) ((?<text2>.*)s|(?<text2>.*))$]]></pattern>
    <script><![CDATA[
      if (IsInt(text1)) {
        amount = ToInt(text1)
      }
      else {
        error ("Expected a number, but got "+text1+".")
      }
      regex = "(?<object>.+)"
      if (not IsRegexMatch(regex, text2)) {
        error ("Object not found.")
      }
      dict = Populate(regex, text2)
      object = GetObject(StringDictionaryItem(dict, "object"))
      found = false
      poss = NewObjectList()
      foreach (obj, ScopeReachableNotHeld()) {
        if (GetDisplayAlias(obj) = GetDisplayAlias(object)) {
          if (HasAttribute(obj,"multiple")) {
            found = true
            msg ("Found possible match: "+obj)
            list add (poss, obj)
          }
          else {
            if (text1 = "1") {
              DoTake (obj, multiple)
              game.lastobjects = NewObjectList()
              list add (game.lastobjects, obj)
              // Using a return to exit this script
              return (true)
            }
            else {
              msg ("You can't do that.")
              // Using a return to exit this script
              return (false)
            }
          }
        }
      }
      held = false
      if (not found) {
        foreach (obj, ScopeInventory()) {
          if (obj.alias = object) {
            held = true
            msg ("You're already carrying "+obj.article+".")
            return (false)
          }
        }
      }
      if ((not found) and not held) {
        msg ("I can't see that.")
        return (false)
      }
      objects = NewObjectList()
      msg ("amount = "+amount)
      while (amount>0) {
        amount = amount - 1
        msg ("amount = "+amount)
        object = poss[amount]
        msg ("MATCH: "+object)
        list add (objects, object)
      }
      foreach (obj, objects) {
        // AddToInventory (obj)
        DoTake (obj, multiple)
      }
      msg (text1)
      msg (object)
      msg ("Done.")
    ]]></script>
  </command>
  <function name="ListDuplicates" parameters="myList" type="boolean"><![CDATA[
    Log ("Called ListDuplicates")
    // I think this will only find ONE thing with duplicates, then stop. \
    // but this is currently untested.
    i = 0
    foreach (item, myList) {
      Log ("<hr/>item: "+item)
      newList = ListExclude(myList,"")
      list remove (newList, item)
      foreach (thing, newList) {
        Log ("thing: "+thing)
        if (item = thing) {
          Log ("Found one!")
          i = i + 1
          Log ("i = "+i)
        }
      }
      if (i>0) {
        i = i + 1
        // Log (myList)
        Log ("Found "+i+" "+item+"s.")
        return (true)
      }
    }
    // Log (myList)
    Log ("Found "+i)
    return (false)
  ]]></function>
</asl>

For GET you have this pattern:

^get (?<text1>\d+) ((?<text2>.*)s|(?<text2>.*))$

Why are you not matching to an object, as you do for the drop command? You are allowing for plurals, but that should be possible with this (though I have no tested it):

^get (?<text1>\d+) (?<object>.*)s?$

In fact, I would be tempted to include the plural as a synonym for each object, as often it is not as simple as adding an "s". Will there be many objects this will be relevant to?

Also, you check if text1 is a number; it will be because it matched \d+ (and I see you do not bother in the drop command).


If I match an object, it brings up the disambiguation menu.

...but that would be solved if I included a plural as an alt, I bet. (Look at you go, Pixie!)


I didn't think of using regex at first, so I was checking if text1 was an integer in the first script and never changed it. (Hehehe.)


I was considering making a reverse ToWords function, so the player could use GET TWO CHIPS or GET 2 CHIPS.


often it is not as simple as adding an "s".

You speak the truth.

The plural of "mouse" is "meese", not "meeses" (which I hate to pieces).

(That's a joke, kids. It's "mice". That was a reference to an old cartoon.)

Anyway... I hadn't even thought of that, but you solved that when you suggested plurals as alternate names.


Thanks, Pix!

I'll post the revised version soon!


NOTE

Most of this is what I recall from something mrangel previously posted.

SIDE-NOTE

It's what I recall, meaning it's most likely a sloppy version of what I'm misremembering.

So the awesome concept is mrangel's, but the bad scripting is mine.


(Just making sure the credit is properly given.)


I was considering making a reverse ToWords function, so the player could use GET TWO CHIPS or GET 2 CHIPS.

That would be good; some people are sure to type that. It may not be easy, though, as Quest will try to match GET RED BALLOON, and may not realise RED does not match a number until too late (i.e., it is already commited to that command). Hmm, I guess you could have the words in regex like this maybe (untested!):

^get (?<text1>(\d+|one|two|three)) ((?<text2>.*)s|(?<text2>.*))$

I do some coding in Ruby on Rails, and Rails has a built in mechanism for making plurals, which is cool... until you do some odd, and it, for example, tries to pluralise meta-data as meta-datas.


I added plurals as alt names, but it still brought up the disambiguation menu, which is what I'm trying to avoid here, of course.

Now, I know that the disambiguation menu is my friend, so I'm not downing it. I just want to let it rest when I want to pickup numerous chips in one TAKE.

The way I have it set up now, it does this. It will TAKE OBJECT normally, or TAKE n OBJECTS, and the same goes for dropping objects.

The main problem (I think, since the alt names should resolve any issues with irregular plurals) is the (?<text2>.*)s, which is sort of allowing a text2 which ends in "s" to slide through before checking if text2 matches an object after dropping the "s".

Doesn't Quest already do that?

When a CHIP is in the room, I can enter GET CHI, and Quest will assume I meant CHIP, unless there's a CHICKEN or CHITLIN or just some plain old CHI in the room. (Hehehe.)

So, what's the difference in letting a word slip through as text then immediately checking it against the regex (?<object>.+)?

Well, first off, I was thinking backwards.

Quest will match when you enter fewer characters, but you can't ADD any characters! (Dur!)


Also, I am now envisioning a scenario:

GET 2 BATONS

I didn't test that! (Doh!)

> get 2 batons
Found possible match: Object: baton
amount = 2
amount = 1
Error running script: Error evaluating expression 'poss[amount]': Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index


Damn and blast!

I'll try adding one more if to catch it.

...and it worked.

Now you can GET 1 BATON, even when the baton isn't set as a multiple.

If you try to get more than 1, it says, "You can't do that."


Is this more dangerous, or less dangerous than the first script?

  <command name="take_n_objects">
    <pattern type="string"><![CDATA[^get (?<text1>\d+) ((?<text2>.*)s|(?<text2>.*))$]]></pattern>
    <script><![CDATA[
      if (IsInt(text1)) {
        amount = ToInt(text1)
      }
      else {
        error ("Expected a number, but got "+text1+".")
      }
      regex = "(?<object>.+)"
      if (not IsRegexMatch(regex, text2)) {
        error ("Object not found.")
      }
      dict = Populate(regex, text2)
      object = GetObject(StringDictionaryItem(dict, "object"))
      found = false
      poss = NewObjectList()
      foreach (obj, ScopeReachableNotHeld()) {
        if (GetDisplayAlias(obj) = GetDisplayAlias(object)) {
          if (HasAttribute(obj,"multiple")) {
            found = true
            msg ("Found possible match: "+obj)
            list add (poss, obj)
          }
          else {
            if (text1 = "1") {
              DoTake (obj, multiple)
              game.lastobjects = NewObjectList()
              list add (game.lastobjects, obj)
              // Using a return to exit this script
              return (true)
            }
            else {
              msg ("You can't do that.")
              // Using a return to exit this script
              return (false)
            }
          }
        }
      }
      held = false
      if (not found) {
        foreach (obj, ScopeInventory()) {
          if (obj.alias = object) {
            held = true
            msg ("You're already carrying "+obj.article+".")
            return (false)
          }
        }
      }
      if ((not found) and not held) {
        msg ("I can't see that.")
        return (false)
      }
      objects = NewObjectList()
      msg ("amount = "+amount)
      while (amount>0) {
        amount = amount - 1
        msg ("amount = "+amount)
        object = poss[amount]
        msg ("MATCH: "+object)
        list add (objects, object)
      }
      foreach (obj, objects) {
        // AddToInventory (obj)
        DoTake (obj, multiple)
      }
      msg (text1)
      msg (object)
      msg ("Done.")
    ]]></script>
  </command>

ANSWER

Both scripts are flawed.

I thought this would catch it if text2 wasn't an object, but it doesn't:

regex = "(?<object>.+)"
if (not IsRegexMatch(regex, text2)) {
  error ("Object not found.")
}

When there is no WIDGET, I can put GET WIDGET and it says, "I can't see that," just as it should, but GET 1 WIDGET or GET 2 WIDGETS causes problems.


Curses!!!

I'll try adding one more foreach(obj,ScopeReachableNotHeld()) to catch it before giving up.


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

Support

Forums