Linking items together

Konisforce
12 Jul 2016, 20:30

I'm looking for a way to link items together so they can influence each other, and have it be robust enough that it can handle things being performed in different orders.

An example;

Say I have a room with a computer, a generator, and a gas can. I want the player to have to fill up the generator with gas, start it, and plug the computer into it before the computer can be used.

I can't just have the computer check if it's plugged in, because the generator could be empty. I can't have the computer check if the generator is full only when it's plugged in, because the player could plug the computer in, then go to turn on the generator. So the computer needs to check the state of the generator when it turns on. For the same reasons, I can't have the generator pass information just when a state changes.

Then say the player takes the generator with them for some reason. I can't have the computer's "on" state persist, because the thing is gone now.

Any smart way to do this that doesn't involve piles of "if" statements?


The Pixie
12 Jul 2016, 21:19

I would create a function that checks all these things, and does the behavior as appropriate, and then have each item call the function when its thing is done. You will only have to do all your ifs once then. And I think large numbers of ifs are ineviable in Interactuive Fiction. Why do you think if is called IF?

(That is kind oif vague; just ask if you need more detail).


XanMag
13 Jul 2016, 00:08

I'm a total moron and for that I will apologize... but, for some reason, I had a hell of a time getting this to work. Eventually I did and the code is below. It just seems so convoluted. If any of you have time, please take a look at it and tell me if there is an easy way to streamline this mess. It would be helpful as it is something I do in my games quite a bit. If I can get this 'creation' down to 10-15 minutes as opposed to 60, I'd be a happy camper. Thanks.

<object name="special order room">
    <inherit name="editor_room" />
    <object name="computer">
      <inherit name="editor_object" />
      <inherit name="switchable" />
      <look type="script">
        if (GetBoolean(computer, "plugged")) {
          if (IsSwitchedOn(computer)) {
            msg ("The computer is plugged into the generator and is currently on.")
          }
          else {
            msg ("The computer is plugged into the generator but is turned off.")
          }
        }
        else {
          msg ("The computer sits unplugged and is, thus, obviously turned off.  You could 'plug in computer' and see what happens?")
        }
      </look>
      <takemsg>Once you have it powered up, just use it.  Too cumbersome to carry around anyway.</takemsg>
      <feature_usegive />
      <use type="script">
        if (IsSwitchedOn(computer)) {
          msg ("You surf the internet, watching hours of videos of funny cats... You decide that was enough wasted time and get back to the task at hand - learning the Quest software!")
        }
        else {
          if (GetBoolean(computer, "plugged")) {
            msg ("Yoou'll need to turn it on first.")
          }
          else {
            msg ("It's not even plugged in you goon.  This laptop does not have a battery either.")
          }
        }
      </use>
      <feature_switchable />
      <onswitchon type="script"><![CDATA[
        if (GetBoolean(computer, "powered")) {
          msg ("...the computer and it springs to life.")
          SwitchOn (computer)
        }
        else if (not GetBoolean(computer, "plugged")) {
          msg ("...nothing.  Nothing happens.  Perhaps you should 'plug computer into generator' first.<br/><br/>You flip the switch back to off.")
          SwitchOff (computer)
        }
        else {
          msg ("...odd.  Nothing happened.  Even though it is plugged into the generator, it doesn't not seem to be getting any power.<br/><br/>You flip the switch back to off.")
          SwitchOff (computer)
        }
      ]]></onswitchon>
      <onswitchoff type="script">
        SwitchOff (computer)
      </onswitchoff>
      <switchonmsg>You switch it on and...</switchonmsg>
      <switchoffmsg>You switch the computer back off.</switchoffmsg>
      <alt type="stringlist">
        <value>comp</value>
        <value>cpu</value>
      </alt>
    </object>
    <object name="generator">
      <inherit name="editor_object" />
      <inherit name="switchable" />
      <look type="script">
        if (IsSwitchedOn(generator)) {
          msg ("It is a gas-powered generator that you have filled with gasoline and started.  It hums quite nicely as it is ready to power your electrical appliances.")
        }
        else {
          if (GetBoolean(generator, "filled")) {
            msg ("It is a gas-powered generator that has been filled with gasoline but is not turned on.  To turn it on, just push the button!")
          }
          else {
            msg ("It's a gas-powered generator that is both empty of gasoline and turned off.  There is a little button on it that should start it up (assuming there is gas in it).  To turn it on, just make sure it has gas in it and 'push button' or 'turn on generator.")
          }
        }
      </look>
      <feature_switchable />
      <onswitchon type="script">
        if (GetBoolean(generator, "on")) {
          msg ("It's already on, dingleberry.")
        }
        else {
          power to generator
        }
      </onswitchon>
      <onswitchoff type="script">
        msg ("There is no need to turn off the generator.  You've got it running and that was your goal!")
      </onswitchoff>
      <takemsg>Too heavy.</takemsg>
    </object>
    <object name="gas can">
      <inherit name="editor_object" />
      <look type="script">
        if (GetBoolean(gas can, "filled")) {
          msg ("It's full of gas.  You can use this on the generator now!")
        }
        else {
          msg ("It's really just an open-mouthed can and you could certainly fill it with anything you'd like, including gasoline...")
        }
      </look>
      <take />
      <feature_usegive />
      <use type="script">
        if (GetBoolean(gas can, "filled")) {
          msg ("I assume you want to use the gas can on the generator?  Okay then.  You empty the gas can into the generator and toss the old, stinky can away.")
          RemoveObject (gas can)
          SetObjectFlagOn (generator, "filled")
        }
        else {
          msg ("You try to wear the gas can as a helmet, but it doesn't fit your big head.  Oh... wait.  You want to probably use it on the generator, right?  Well, you will need to get gas in it first.  Use the pump.")
        }
      </use>
      <selfuseon type="scriptdictionary">
        <item key="generator">
          if (GetBoolean(gas can, "filled")) {
            msg ("You empty the gas can into the generator and toss the old, stinky can away.")
            RemoveObject (gas can)
            SetObjectFlagOn (generator, "filled")
          }
          else {
            msg ("You rattle the empty gas can against the generator but nothing happens.  Wait a second!  You want to put fuel in the generator?  Yes, of course.  But, that won't happen unless you have gas in the can first! ")
          }
        </item>
      </selfuseon>
    </object>
    <object name="pump">
      <inherit name="editor_object" />
      <look>It looks like an old water pump, but because of the smell of gasoline, you safely assume that it is an oil pump.  To use the pump, just type 'use pump'.</look>
      <feature_usegive />
      <use type="script">
        if (Got(gas can)) {
          msg ("You place the gas can under the pump and crank the lever a few times.  Gas spills out and fills your can.  Now you could use that on the generator!")
          SetObjectFlagOn (gas can, "filled")
        }
        else {
          msg ("You consider using the pump, but seeing that you are not carrying anything to pump the gas into, you decide against it.")
        }
      </use>
      <takemsg>You cannot take it.  It is fastened firmly to the ground.</takemsg>
    </object>
    <object name="button1">
      <inherit name="editor_object" />
      <alias>button</alias>
      <scenery />
      <look>It's a little button attached to the generator.  Push it, if you haven't already gotten the generator started.</look>
      <push type="script">
        if (GetBoolean(generator, "on")) {
          msg ("It's already on, dingleberry.")
        }
        else {
          power to generator
        }
      </push>
    </object>
    <command name="plug CPU cmd">
      <pattern>plug in computer; plug in the computer; use computer with generator; plug in computer to generator; use computer on generator; use generator on computer; plug computer into generator; plug in comp; plug in the comp; use comp with gen; plug in comp to gen; use comp on gen; use gen on comp; plug comp into gen</pattern>
      <script>
        msg ("You plug the computer into the generator.")
        SetObjectFlagOn (computer, "plugged")
        if (IsSwitchedOn(generator)) {
          SetObjectFlagOn (computer, "powered")
        }
      </script>
    </command>
    <command name="unplug CPU">
      <pattern>unplug computer; unplug the computer; unplug computer from generator</pattern>
      <script>
        if (GetBoolean(computer, "plugged")) {
          msg ("You pull the plug out of the generator.")
          SetObjectFlagOff (computer, "plugged")
          SetObjectFlagOff (computer, "powered")
          SwitchOff (computer)
        }
        else {
          msg ("It's not plugged in to begin with, dummy!")
        }
      </script>
    </command>

And here is the function.

    if (not IsSwitchedOn(generator)) {
      if (GetBoolean(generator, "filled")) {
        msg ("The generator fires right up.  It's ready to power your appliances.")
        SwitchOn (generator)
        SetObjectFlagOn (generator, "on")
        if (GetBoolean(computer, "plugged")) {
          SetObjectFlagOn (computer, "powered")
        }
      }
      else {
        msg ("You try to start the generator by pushing the button, but nothing happens.  I think it needs some gas.")
      }
    }
  </function>

Anyway... any help would be appreciated. I know there is a lot going on here, but this seems to be a fairly common question on the forums: How to check to make sure the player completes tasks in a specific order. If they events are simply completing a number of tasks, I get that. Five tasks need done before a door opens in any order. Easy. But, in this case, several flags on each objects need set in a specific order and each needs checked. Just looking for a streamlining option. Specifically, here's what was done in the code I finished.

  1. Fill gas can by using on pump.
  2. Use the gas can on the generator (flag name 'filled' on generator)
  3. Turn on generator (also could push button on generator (called 'power to generator' function). If computer is already plugged in to generator when turning on generator (flag name 'powered' to computer), it may be switched on and used.
  4. Plug in computer (flag name 'plugged' on computer) and if generator is on (flag name 'powered' on computer).
    Once the computer is 'powered', it can be turned on and used. I cheated a bit and didn't allow the generator to be turned off, but I did unset all flags on the computer when it is unplugged.

I had a specific problem with trying to turn the generator on and getting duplicate messages that it has been turned on already. It was because I need to turn on the generator when switched on, but I also had a message print saying it was already on IF it was already on... UGH. I solved it by adding a flag named 'on' to the generator and checking it BEFORE calling the 'power to generator' function in order to get the message to print once. I originally checked for to see if the generator was switched on in the function.

Each option has been accounted for here if done in the incorrect order... trying to use the pump without the can, turning on/using the computer without it being plugged in, plugged in but no powered, and trying to turn on the generator/pushing button without filling the generator with gas. It seems simple when written that way, but...

@Konisforce,

If this will suffice (it DOES work, it's just a jumbled mess of code), feel free to copy-paste it into your game. If you would like the .aslx file of it I can send that, too. Or, obviously, if you have questions, please feel free to ask.

Good luck!
XanMag


The Pixie
13 Jul 2016, 07:46

Further to what I said before, I am not sure a function is the best way to do this. It is only when the player tries to turn on the computer that you need to test if everything else is done.

XanMag, there is not much you can do to simplify. It is a complicated thing, so there is going to be messy code. A few tweaks, most of which will make it more cmplicated:

For the gas can, use with generator, using this code (and it will run the code you already have for use on its own):

do(this, "use")

Be good if you handled this command:
fill generator

For the generator, you can turn it on without filling with fuel! This is, perhaps, a problem with Quest, which only gives the casual user access ro what happens AFTER it is turned on or off. Looks like you recognised this with the computer, as that handles the issue. Also the "power to generator" code can go in the script (you probably put it in a function because of my bad advice...).

For the "plug CPU cmd", I would not bother with the computer.powered flag (and modify the switchon code in the computer), but you should check if it is already plugged in, as you already do with unplugging (also, it is not a CPU you are plugging in, that is a small component on the motherboard inside the computer!)


hegemonkhan
13 Jul 2016, 13:21

@everyone/XanMag:

if you have a lot of combinations/conditions to handle, you have a lot of combinations/conditions to handle (and thus lots of messy/verbose: ifs~else ifs~elses). There's not much you can do about this, unfortunately. As Pixie said in his/her post, ~ "There's a reason interactive fiction is called/known as: IF"


Konisforce
13 Jul 2016, 15:17

@XanMag -

Thanks for that! I'll do it myself for the practice, but that's a good outline to have. And unfortunately is about what I thought it would be. I am doing a lot of this in my quest (it's a training scenario) so might need to do a custom "attach / unattach" command, that tracks states like this and allows things to be referenced up and down the chain. Looks like a lot of flag-setting and "if"s is the way it's gotta be.