Vending Machine (sodas only (at the moment)) [WORK IN PROGRESS]

K.V.

Hello, everyone.

Point out the flaws, please.

http://textadventures.co.uk/games/view/r-a9zqtml0kxj5vywas_-g/vending-machine

<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <dynamictemplate name="ObjectNotOpen">"The "+GetDisplayAlias(object) + " " + Conjugate(object, "be") + " not open."</dynamictemplate>
  <game name="Vending Machine">
    <gameid>ac777d98-ee5d-4371-a0c2-353536868b35</gameid>
    <version>1.0</version>
    <firstpublished>2017</firstpublished>
    <showmoney />
    <start type="script">
      CloneDollarAndMove (break_room)
      CloneDollarAndMove (break_room)
    </start>
  </game>
  <object name="break_room">
    <inherit name="editor_room" />
    <alias>break room</alias>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <object name="vending_machine">
      <inherit name="editor_object" />
      <inherit name="container_open" />
      <feature_container />
      <open type="boolean">false</open>
      <close type="boolean">false</close>
      <displayverbs type="stringlist">
        <value>Look at</value>
        <value>Take</value>
      </displayverbs>
      <takemsg>It's much too big.</takemsg>
      <alias>Vending Machine</alias>
      <credit type="int">0</credit>
      <isopen />
      <transparent type="boolean">false</transparent>
      <hidechildren />
      <listchildren />
      <look><![CDATA[The machine has {vending_machine.credit} credit{if vending_machine.credit<>1:s}.<br/><br/>On it, you can see {object:buttons} and a {object:slot}, and there is a {object:tray} at the bottom.]]></look>
      <addscript type="script"><![CDATA[
        if (not object.prototype = quest_buck) {
          msg (CapFirst(object.article) + " doesn't fit into the slot.")
        }
        else {
          vending_machine.credit = vending_machine.credit + 1
          msg ("Done.<br/>The machine has {vending_machine.credit} credit{if vending_machine.credit<>1:s} now.")
          game.pov.money = game.pov.money - 1
          RemoveObject (object)
        }
      ]]></addscript>
    </object>
    <object name="slot">
      <inherit name="editor_object" />
      <inherit name="container_open" />
      <scenery />
      <look><![CDATA[It has something written just above it.<br/><br/>It reads:  "INSERT ONE QUEST BUCK".]]></look>
      <feature_container />
      <open type="boolean">false</open>
      <close type="boolean">false</close>
      <displayverbs type="stringlist">
        <value>Look at</value>
      </displayverbs>
      <takemsg>It's part of the machine.</takemsg>
      <addscript type="script"><![CDATA[
        if (not object.prototype = quest_buck) {
          msg (CapFirst(object.article) + " doesn't fit into the slot.")
        }
        else {
          vending_machine.credit = vending_machine.credit + 1
          msg ("Done.<br/>The machine has {vending_machine.credit} credit{if vending_machine.credit<>1:s} now.")
          game.pov.money = game.pov.money - 1
          RemoveObject (object)
        }
      ]]></addscript>
    </object>
    <object name="buttons">
      <inherit name="editor_object" />
      <takemsg>They are part of the machine.</takemsg>
      <scenery />
      <look>There are two buttons: a {object:cola_button:Quest Cola} button and a {object:diet_cola_button:Diet Quest Cola} button.</look>
      <displayverbs type="stringlist">
        <value>Look at</value>
      </displayverbs>
    </object>
    <object name="diet_cola_button">
      <inherit name="editor_object" />
      <scenery />
      <takemsg>It's part of the machine.</takemsg>
      <alias>Diet Quest button</alias>
      <look>It reads: "DIET QUEST".</look>
      <displayverbs type="stringlist">
        <value>Look at</value>
      </displayverbs>
      <press type="script"><![CDATA[
        if (vending_machine.credit>0) {
          BuyDrink (diet_cola_can)
        }
        else {
          msg ("The vending machine doesn't have any credits.")
        }
      ]]></press>
      <push type="script">
        invoke (this.press)
      </push>
    </object>
    <object name="cola_button">
      <inherit name="editor_object" />
      <takemsg>It's part of the machine.</takemsg>
      <scenery />
      <alias>Quest Cola button</alias>
      <look>It reads: "QUEST COLA".</look>
      <displayverbs type="stringlist">
        <value>Look at</value>
      </displayverbs>
      <press type="script"><![CDATA[
        if (vending_machine.credit>0) {
          BuyDrink (cola_can)
        }
        else {
          msg ("The vending machine doesn't have any credits.")
        }
      ]]></press>
      <push type="script">
        invoke (this.press)
      </push>
    </object>
    <object name="tray">
      <inherit name="editor_object" />
      <inherit name="container_closed" />
      <scenery />
      <feature_container />
      <takemsg>It's part of the machine.</takemsg>
      <close type="boolean">false</close>
      <displayverbs type="stringlist">
        <value>Look at</value>
        <value>Open</value>
      </displayverbs>
      <look><![CDATA[A black tray {either tray.isopen:(currently open)|that swivels in when you push it.<br/><br/>It reads: "PUSH TO OPEN"}.]]></look>
      <onopen type="script"><![CDATA[
        SetTurnTimeout (4) {
          tray.isopen = false
          msg ("<br/>The tray just closed.")
        }
      ]]></onopen>
      <addscript type="script">
        msg ("Putting anything in there would be pointless.")
      </addscript>
      <push type="script">
        invoke (tray.press)
      </push>
      <press type="script">
        if (not tray.isopen) {
          msg ("The tray opens.")
          tray.isopen = true
          // invoke (tray.onopen)
        }
        else {
          msg ("It's already open.")
        }
      </press>
      <listchildren />
    </object>
  </object>
  <verb>
    <property>press</property>
    <pattern>press</pattern>
    <defaultexpression>"You can't press " + object.article + "."</defaultexpression>
  </verb>
  <command name="no_use">
    <pattern>use #object#</pattern>
    <script>
      msg ("I don't understand your command.")
    </script>
  </command>
  <object name="cola_can">
    <inherit name="editor_object" />
    <inherit name="container_closed" />
    <alias>can of Quest Cola</alias>
    <take />
    <feature_container />
    <close type="boolean">false</close>
    <openmsg>You pop the top.</openmsg>
    <instock type="int">2</instock>
    <look>A Quest Cola can.</look>
    <displayverbs type="stringlist">
      <value>Look at</value>
      <value>Take</value>
      <value>Open</value>
    </displayverbs>
    <drink type="script"><![CDATA[
      if (this.isopen) {
        if (ListCount(GetDirectChildren(this))>0) {
          foreach (drink, GetDirectChildren(this)) {
            if (HasAttribute(drink,"drink")) {
              do (drink, "drink")
              return (true)
            }
          }
        }
        else {
          msg ("It's empty.")
        }
      }
      else {
        msg ("It isn't open.")
      }
    ]]></drink>
    <object name="cola">
      <inherit name="editor_object" />
      <drinks type="int">6</drinks>
      <alias>cola</alias>
      <look>It's brown.</look>
      <smell>It smells sweet and fizzity!</smell>
      <taste>Not bad.</taste>
      <drink type="script">
        DrinkCola (this)
      </drink>
    </object>
  </object>
  <object name="diet_cola_can">
    <inherit name="editor_object" />
    <inherit name="container_closed" />
    <alias>can of Diet Quest Cola</alias>
    <take />
    <feature_container />
    <close type="boolean">false</close>
    <openmsg>You pop the top.</openmsg>
    <look>A Diet Quest Cola can.</look>
    <displayverbs type="stringlist">
      <value>Look at</value>
      <value>Take</value>
      <value>Open</value>
    </displayverbs>
    <instock type="int">4</instock>
    <drink type="script"><![CDATA[
      if (this.isopen) {
        if (ListCount(GetDirectChildren(this))>0) {
          foreach (drink, GetDirectChildren(this)) {
            if (HasAttribute(drink,"drink")) {
              do (drink, "drink")
              return (true)
            }
          }
        }
        else {
          msg ("It's empty.")
        }
      }
      else {
        msg ("It isn't open.")
      }
    ]]></drink>
    <object name="diet_cola">
      <inherit name="editor_object" />
      <drinks type="int">6</drinks>
      <alias>diet cola</alias>
      <look>It's brown.</look>
      <smell>It smells fizzity!</smell>
      <taste>Not bad.</taste>
      <drink type="script">
        DrinkCola (this)
      </drink>
    </object>
  </object>
  <object name="quest_buck">
    <inherit name="editor_object" />
    <take />
    <look><![CDATA[One Quest Buck.  <br/><br/>It has a picture of Alex Warren on one side, and a big <span style='font-size:200%;'> Q</span> on the other.<br/><br/>(It's worth one unidollar.)]]></look>
    <alias>Quest Buck</alias>
    <alt type="stringlist">
      <value>dollar</value>
      <value>money</value>
    </alt>
    <ontake type="script">
      game.pov.money = game.pov.money + 1
    </ontake>
  </object>
  <function name="BuyDrink" parameters="drink"><![CDATA[
    if (drink.instock > 0) {
      vending_machine.credit = vending_machine.credit - 1
      drink.instock = drink.instock - 1
      thisClone = CloneObjectAndMove(drink,tray)
      thisClone.prototype = drink
      msg ("The machine drops a can into the tray.")
    }
    else {
      msg ("The machine is sold out of "+GetDisplayAlias(drink)+"s.")
    }
  ]]></function>
  <function name="DrinkCola" parameters="this"><![CDATA[
    if (this.drinks>0) {
      msg ("You take a drink.")
      this.drinks = this.drinks - 1
      if (this.drinks<1) {
        msg ("  The can is now empty.")
        this.parent.alias = "empty can"
        if (HasString(this.parent,"look")) {
          this.parent.look = this.parent.look + "<br/><br/>It is empty."
        }
        RemoveObject (this)
      }
    }
    else {
      msg ("It's all gone.")
      this.parent.alias = "empty can"
      if (HasString(this.parent,"look")) {
        this.parent.look = this.parent.look + "<br/>"+CapFirst(object.article)+"is empty."
      }
      RemoveObject (this)
    }
  ]]></function>
  <function name="CloneDollarAndMove" parameters="room">
    newDollar = CloneObjectAndMove(quest_buck,room)
    newDollar.prototype = quest_buck
  </function>
</asl>

One of the displayed verbs for the tray is "push":

> push tray
Error running script: Error compiling expression 'tray.open': RootExpressionElement: Cannot convert type 'Boolean' to expression result of 'IScript'

The state of the tray should be noted in the descripotion; whether open and if there is something in it (I guess the can can be seen even if closed).

Should understand "put money in machine", if only to say it needs to go in slot.

When the tray closes, you get told twice.

The cannot see the can in the tray, even if the tray is open.

Apart from that, looks good!

Be useful to add another coin and see how it handles two credits.


K.V.

Thanks, Pix!

Just so people can learn from my errors:


>push tray
Error running script:

I'd set up press tray, and I meant to make push tray invoke tray.press, but I put invoke (tray.open), which tries to run a Boolean attribute as a script, which throws an error.

[FIXED]


The state of the tray should be noted in the description; whether open and if there is something in it (I guess the can can be seen even if closed).

Easy fix:

A black tray {either tray.isopen:(currently open)|that swivels in when you push it.

It reads: "PUSH TO OPEN"}.

[FIXED]


When the tray closes, you get told twice.

I didn't have anything checking if the tray was open before running the script which opens it (which sets a turn timeout before it automatically closes).

if (not tray.isopen) {
  msg ("The tray opens.")
  tray.isopen = true
  invoke (tray.onopen)
}
else {
  msg ("It's already open.")
}

Also, it prints the message twice because I have an open message set up, and I was calling that change script twice:

if (not tray.isopen) {
  msg ("The tray opens.")
  tray.isopen = true   //This called the change script the first time.
  //invoke (tray.onopen) //This called the change script a second time.
}
else {
  msg ("It's already open.")
}

[FIXED]


cannot see the can in the tray, even if the tray is open.

I didn't have List children when object is looked at or opened selected. (Doh!)

[FIXED]


Apart from that, looks good!

Thanks!!!


Be useful to add another coin and see how it handles two credits.

An excellent idea.

I changed this script to check if the object has the attribute prototype set to quest_buck, and I cloned the dollar to test it out, and it worked well.


Should understand "put money in machine", if only to say it needs to go in slot.

I had this working initially, but I set the machine (which is a container) to closed after I put the drinks in it, so it broke.

...I guess there's no sense in the drinks actually being in the vending machine, huh?

ONE MINUTE LATER...

I put the drinks under the object object, and set the machine to open.

Now putting the money in the machine works just like putting it into the slot.

[FIXED]


Now, I have to learn how to make two or more Quest Bucks automatically become Quest Bucks, which shows 2 Quest Bucks or 3 Quest Bucks, or what have you.

Then, I want to be able to do this (pretend we start with 4 Quest Bucks):

>put 1 Quest Buck in slot
You put one Quest Buck into the slot.
(You now have 3 Quest Bucks.)


The posted code has been updated.


K.V.

I must learn how to adapt this Inform 7 code to Quest:

After deciding the scope of the player while quest_bucks is carried by the player: place quest_buck in scope.

Maybe something like:

if(Got(quest_bucks)){
  if (HasAttribute(game, "scopebackdrop")){
    game.scopebackdropbak = game.scopebackdrop
  }
  game.scopebackdrop => {
    list add (items, quest_buck)
  }
  quest_buck.scenery = true
}


Okay... I have no idea what either version is supposed to do.


K.V.

> I have no idea what either version is supposed to do.

That's probably because I'm not thinking about it right.

I was thinking:

If the player is carrying Quest Bucks (multiple), then a Quest Buck is in scope, but not actually in the inventory.

Then, one could enter put Quest Buck in slot. (When this happened, Quest Bucks would be decreased by 1.)

...but that would probably bring up the disambiguation menu...

Do you mean?

1. Quest Bucks
2. Quest Buck


Basically, I want to have n Quest Bucks, and be able to enter put 1 Quest Buck in slot or put 2 Quest Bucks in slot.

So... I guess the scope has jack to do with squat.

Hrmm...

Do I need to study up on stacking? Is that what stacking is? (Rhetorical. I'm going to look it up right now. (Do feel free to chime in, though. I just don't want to put anyone out whilst I'm reading up on stacking in this very forum.))


Here's an example of what that scope script would do (although it has nothing to do with money or vending machines):

<!--Saved by Quest 5.7.6404.15496-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="No Commands">
    <gameid>94a496f3-563a-4a94-8e31-0c9b7390cff6</gameid>
    <version>1.0</version>
    <firstpublished>2017</firstpublished>
  </game>
  <object name="your teepee">
    <inherit name="editor_room" />
    <enter type="script">
    </enter>
    <usedefaultprefix type="boolean">false</usedefaultprefix>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <exit alias="out" to="field">
      <inherit name="outdirection" />
    </exit>
    <object name="black_shard">
      <inherit name="editor_object" />
      <alias>black shard of ∞</alias>
      <look>It's the black shard, which gives you the power to see magically protected realms.</look>
      <take />
      <drop type="boolean">false</drop>
      <dropmsg>You wouldn't dare drop such a valuable item.</dropmsg>
    </object>
  </object>
  <object name="enchanted castle">
    <look type="script"><![CDATA[
      msg ("You close your eyes and concentrate on the castle.  You feel a sudden shift in reality.<br/>")
      MoveObject (game.pov, enchanted castle)
      finish
    ]]></look>
    <description><![CDATA[<br/>This is entrance to an enchanted castle.<br/><br/>KV is lazy and did not fill in very many details, seeming how this is just an example game.<br/><br/><center><h3>CONGRATULATIONS!</h3><h1>YOU HAVE WON!</h1></center>]]></description>
    <displayverbs type="stringlist">
      <value>Look at</value>
    </displayverbs>
  </object>
  <object name="field">
    <inherit name="editor_room" />
    <enter type="script">
    </enter>
    <beforeenter type="script"><![CDATA[
      if (Got(black_shard)) {
        if (HasAttribute(game, "scopebackdrop")) {
          game.scopebackdropbak = game.scopebackdrop
        }
        game.scopebackdrop => {
          list add (items, enchanted castle)
        }
      }
    ]]></beforeenter>
    <onexit type="script">
      if (HasAttribute(game, "scopebackdropbak")) {
        game.scopebackdrop = game.scopebackdropbak
      }
      else {
        game.scopebackdrop = null
      }
    </onexit>
    <description><![CDATA[<br/>This vast field appears to go on forever in every direction.{either black_shard.parent=game.pov:<br/><br/>You can see a {object:enchanted castle} off in the distance.|}]]></description>
    <exit alias="in" to="your teepee">
      <inherit name="indirection" />
    </exit>
    <object name="teepee_outside">
      <inherit name="editor_object" />
      <scenery type="boolean">false</scenery>
      <alias>your teepee</alias>
      <usedefaultprefix type="boolean">false</usedefaultprefix>
      <takemsg>The teepee is too big and bulky to tote around for no good reason.</takemsg>
    </object>
    <command name="enter_teepee">
      <pattern>enter #object#</pattern>
      <script>
        if (object = teepee_outside) {
          MoveObject (game.pov, your teepee)
        }
        else {
          msg ("You can't enter "+object.article+".")
        }
      </script>
    </command>
  </object>
  <walkthrough name="kv">
    <steps type="simplestringlist">
      o
      enter teepee
      get shard
      o
      in
      o
      x castle
    </steps>
  </walkthrough>
</asl>

There would be more than one location from which you could see the enchanted castle in an actual game, of course.

I guess I could just leave the scopebackdrop script alone, only adding the castle once. Then, make it visible or invisible.

...but I digress.

I need to learn to make an object into a group of objects.

Let's say I have an object named poker chip, which exists out of play.

To add a new chip to play, I would do this:

newChip = CloneObjectAndMoveHere(poker chip)
newChip.prototype = poker chip

Now, there is another out-of-play object named poker chips, which I move to the player (or simply make visible) when he or she has more than one poker chip. I then move each poker chip into poker chips.

Then, I could do something like this:

poker chips.count = ListCount(GetDirectChildren(poker chips))
poker chips.alias = ToWords(poker chips.count) + " poker chips"

This is where I get stuck while theorizing this.

I want to then be able to PUT 2 CHIPS ON TABLE or PUT 1 CHIP ON TABLE.


Like I say, there are probably already good examples posted already, I'm just typing and theorizing instead of searching and learning.

I'm off to search now, though!


PS

Sorry. I've had too much sugar today, and I'm all scatter-brained and stuff.


I would do money as an attribute. When the player picks some up, destroy the object and update the attribute. Listing it with a status attributes would be easiest, but you could have a wallet object or whatever.


K.V.

>I would do money as an attribute. When the player picks some up, destroy the object and update the attribute. Listing it with a status attributes would be easiest, but you could have a wallet object or whatever.

I gotcha.

That's what I've got scripted at the moment. (And when I say, "I've got it scripted," that just means I copied it from one of your examples. Hehehe.)


This is my experiment, which combines what Pixie just said with something I vaguely remember mrangel posting a while back:

<!--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.1</version>
    <firstpublished>2017</firstpublished>
    <showmoney />
  </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>
  </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)
            MoveObjectHere (newObj)
            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>
  <walkthrough name="kv">
    <steps type="simplestringlist">
      i
      get all
      i
      x chips
      drop 2 chips
      i
      x chips
      get all
      drop 1 chip
      x
      i
      x chips
      get all
      drop 3 chips
      i
      x chip
      1
      get all
      drop baton
      get it
      drop all
    </steps>
  </walkthrough>
</asl>

I can drop n chips, but I can't make it work on the take script for the life of me.

What I've got for the drop script is all I can recall from an older post by mrangel. (Which means its my bastardized version, so it's more like some code based on a vague memory of a post by mrangel. (Just to do mrangel justice. He didn't write this messy script.))


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

Support

Forums