Question about custom command pane

I added a custom command pane to my game interface and populated it with JS.SetCommands; now I'd want it to have 2 behaviours:
a) It should pass everything I click to a function that will handle it instead of taking it as a direct command;
b) Commands should be enabled or disabled according to the situation: e. g., if there is nothing that can be opened "Open" should be disabled.
How could I make the custom command pane do so?
Thanks.


Cool thing. I have absolutely no idea how, or even if, it's possible but I'll sure be popping in here every now and then to see if someone has answered!


a) It should pass everything I click to a function that will handle it instead of taking it as a direct command;

As far as I know, that's not what the custom command pane is for. I suspect that creating your own custom pane will be easier than modifying one of the built-in ones to behave in a completely different way.

I can see ways to do it, but that would be a pretty big modification.


As far as I know, that's not what the custom command pane is for. I suspect that creating your own custom pane will be easier than modifying one of the built-in ones to behave in a completely different way.

Humpf - I should have known that. Well, I found this tutorial by The Pixie about creating a custom button pane. Using the custom command pane provided by Quest would have required less fiddling with CSS and HTML, but I'll take it as it comes. So, after creating a custom button pane as shown in the tutorial, how can I disable or enable selectively its buttons?


I can show how to modify which commands are displayed in the Commands pane during play.


Why do you want it to behave differently when clicking a command from that pane?


I'm trying to build an interface like the SCUMM that was used in old LucasArts' adventures, where disabled commands appeared grayed out. I'm just wondering if Quest could give me the same look & feel.


In this example game, the JUMP command only shows up in the pane when you are in the second room.


You control what is displayed in the commands pane in this example my modifying the string attribute: game.pane_commands.

I added the functions to make it easier while scripting. I try to do any complicated coding in functions, that way everything else is a breeze.

There are numerous ways to do this, too. This seemed to be the easiest overall.


<!--Saved by Quest 5.7.6606.27193-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Command Pane">
    <gameid>17f0e962-ffb7-4afa-9206-6258b00ef646</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
    <feature_advancedscripts />
    <commandpane />
    <start type="script">
    </start>
    <inituserinterface type="script">
      if (not HasAttribute(game,"pane_commands")) {
        game.pane_commands = "Look;Wait;Jump"
      }
      JS.setCommands (game.pane_commands)
    </inituserinterface>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <beforeenter type="script">
    </beforeenter>
    <enter type="script">
      RemovePaneCommand ("jump")
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
    <exit alias="north" to="second room">
      <inherit name="northdirection" />
    </exit>
  </object>
  <object name="second room">
    <inherit name="editor_room" />
    <beforeenter type="script">
      AddPaneCommand ("Jump")
    </beforeenter>
    <exit alias="south" to="room">
      <inherit name="southdirection" />
    </exit>
  </object>
  <function name="AddPaneCommand" parameters="cmd">
    list = Split(game.pane_commands)
    exists = false
    foreach (c, list) {
      if (LCase(c) = LCase(cmd)) {
        exists = true
      }
    }
    if (not exists) {
      game.pane_commands = game.pane_commands + ";" + cmd
    }
    SetPaneCommands
  </function>
  <function name="RemovePaneCommand" parameters="cmd">
    list = Split(game.pane_commands)
    foreach (c, list) {
      if (LCase(c) = LCase(cmd)) {
        cmd = ";" + c
        game.pane_commands = Replace(game.pane_commands, cmd,"")
      }
    }
    SetPaneCommands
  </function>
  <function name="SetPaneCommands">
    JS.setCommands (game.pane_commands)
  </function>
</asl>

Greying out disabled commands wouldn't be very complicated, I don't think, but that depends on what you mean by "disabled".

If you mean commands that output, "I didn't understand your command."
... that could be done.

If you mean commands that output: "You can't do that."
...that sounds quite difficult.


I think I see what you mean.

I can't recall seeing greyed out buttons in SCUMM games. I think in those games, you click the command and then the object, the reverse of Quest's verb buttons.

Now I'm imagining you want to modify the verbs display under the inventory/objects panes, so that it shows a static set of commands, and greys out the ones that aren't valid for a given object. Have I got the right idea? I think that coild be done in pure javascript.


@K.V.
I mean commands that don't react to a click when they are disabled.
Removing and adding commands to a panel is a way to disable them - but when I add a new command it appears at the bottom of the list, if I'm not mistaken. I'd want commands to keep their order, so I'm asking for a way to disable them without removal.

@mrangel
I'll have to replay some old SCUMM game in order to be sure, but I seem to recall seeing some of the commands greyed out in games like "Maniac Mansion" or the first two "Monkey Island". Not all of them, though, but "Talk to" button was disabled when there wasn't any NPC to talk to. Anyway, I'm not cloning the old SCUMM interface; I'm building my own, taking elements here and there. And yes, my custom pane shows a static set of commands; I'm looking for a way to grey out and disable the ones that don't have any use in that situation, like it happens with buttons in the compass pane.


I mean commands that don't react to a click when they are disabled.

Please define "disabled". Do you mean out of scope (or currently unavailable)? Like a command that only works in a specific room?

Sort of like the compass buttons? (When exits aren't available, the button is disabled.)


Yes, something like that. Commands that only work in a specific room, or with specific objects, e. g., where in a room there are no doors, trunks, cabinets or other openable objects, the "Open" command has to be disabled.


I think there's three main ways to arrange the interface to let the user select a command and an object. You could pick the command then the object, or the object then the command (the way Quest does it), or have both active and a button to press when you've selected both the command and the item you want to use it on.

I assume you're wanting to pick the command first, as you talk about objects available in the room rather than the selected object.
I'm pretty sure I can see how I'd go about doing that now. And most of it could be done just by tweaking the updateList javascript function. You'd have it initially disable all commands, and then enable the ones corresponding to the verbs it sees as it's displaying the object lists.

Would I be right in assuming that the reason you want a custom function rather than executing the command is because you then want the player to choose an object to use the command on? If so, I suspect it doesn't need to be a Quest function at all. You could keep your code in Javascript, and fire the command when the object is chosen. It's just like the verbs panel, but the other way around.

Hope that makes sense; I'm kind of rushing to get some work done on time tonight, so don't have the time to throw together a mockup right now. I might try later.


How about this example:

Old Code
<!--Saved by Quest 5.7.6606.27193-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Commands Pane">
    <gameid>17f0e962-ffb7-4afa-9206-6258b00ef646</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
    <feature_advancedscripts />
    <commandpane />
    <inituserinterface type="script">
      if (not HasAttribute(game,"pane_commands")) {
        game.pane_commands = "Look;Wait;Jump"
      }
      JS.setCommands (game.pane_commands)
    </inituserinterface>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <enter type="script">
      DisablePaneCmd ("Wait")
    </enter>
    <description><![CDATA[<h1>THIS IS ONLY A TEST</h1><br/><br/>At game start, "Wait" is disabled.<br/><br/>Enter {command:TEST} to enable it.<br/><br/>Enter {command:DISABLE} to disable it.]]></description>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <command name="test_cmd">
    <pattern>test</pattern>
    <script>
      EnablePaneCmd ("Wait")
    </script>
  </command>
  <command name="test_disable">
    <pattern>disable</pattern>
    <script>
      DisablePaneCmd ("Wait")
    </script>
  </command>
  <function name="DisablePaneCmd" parameters="cmd">
    JS.eval ("$('.commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).addClass('disabled').data('deactivated', true);}});")
  </function>
  <function name="EnablePaneCmd" parameters="cmd">
    JS.eval ("$('.commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).removeClass('disabled').data('deactivated', false);}});")
  </function>
</asl>

The next post is more like what you're looking for, I think.


One way to disable unavailable commands:

If using the desktop editor, you could add these two functions in code view:

    <function name="DisablePaneCommand" parameters="cmd">
    JS.eval ("$('#commandPane .commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).addClass('disabled').data('deactivated', true);}});")
  </function>
  <function name="EnablePaneCommand" parameters="cmd">
    JS.eval ("$('#commandPane .commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).removeClass('disabled').data('deactivated', false);}});")
  </function>

Then, in User Interface Initialisation, a turn script, AND after entering each room, call this function:

  <function name="UpdatePaneCommands">
    pcmds = Split(game.pane_commands)
    foreach (pc, pcmds) {
      available = false
      foreach (cmd, ScopeCommands()) {
        if (LCase(pc) = LCase(cmd.name)) {
          available = true
        }
      }
      if (not available) {
        DisablePaneCommand (pc)
      }
      else {
        EnablePaneCommand (pc)
      }
    }
  </function>

You'd have to be precise with your command's names, making sure they match what you add to the pane after both strings are converted to lower case.

Here's an example game:

<!--Saved by Quest 5.7.6606.27193-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Commands Pane">
    <gameid>17f0e962-ffb7-4afa-9206-6258b00ef646</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
    <feature_advancedscripts />
    <commandpane />
    <start type="script">
    </start>
    <inituserinterface type="script">
      if (not HasAttribute(game,"pane_commands")) {
        game.pane_commands = "Look;Wait;Jump;Disabled"
      }
      JS.setCommands (game.pane_commands)
    </inituserinterface>
    <roomenter type="script">
      UpdatePaneCommands
    </roomenter>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <description><![CDATA[<h1>THIS IS ONLY A TEST</h1><br/><br/>Enter {command:TEST} to enable "Wait".<br/><br/>Enter {command:DISABLE} to disable it.]]></description>
    <beforeenter type="script">
      this = GetObject("wait")
      this.parent = command_jail
    </beforeenter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <command name="test_cmd">
    <pattern>test</pattern>
    <script>
      this = GetObject("wait")
      this.parent = null
    </script>
  </command>
  <command name="test_disable">
    <pattern>disable</pattern>
    <script>
      this = GetObject("wait")
      this.parent = command_jail
    </script>
  </command>
  <object name="command_jail">
    <inherit name="editor_object" />
    <command name="disabled">
      <pattern>disabled</pattern>
      <script>
        msg ("This should not work!")
      </script>
    </command>
  </object>
  <turnscript name="pane_cmd_turnscript">
    <enabled />
    <script>
      UpdatePaneCommands
    </script>
  </turnscript>
    <function name="DisablePaneCommand" parameters="cmd">
    JS.eval ("$('#commandPane .commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).addClass('disabled').data('deactivated', true);}});")
  </function>
  <function name="EnablePaneCommand" parameters="cmd">
    JS.eval ("$('#commandPane .commandlink').each(function(){if($(this).html()=='" + cmd + "'){$(this).removeClass('disabled').data('deactivated', false);}});")
  </function>
  <function name="UpdatePaneCommands">
    pcmds = Split(game.pane_commands)
    foreach (pc, pcmds) {
      available = false
      foreach (cmd, ScopeCommands()) {
        if (LCase(pc) = LCase(cmd.name)) {
          available = true
        }
      }
      if (not available) {
        DisablePaneCommand (pc)
      }
      else {
        EnablePaneCommand (pc)
      }
    }
  </function>
</asl>

Modified based on KV's script above; if you don't want to be moving commands in and out of scope.
This enables and disables commands based on whether there are any objects in the room with the command in their displayverbs list. So for example "open" will be disabled if there are no openable containers in the current room.

  <function name="UpdatePaneCommands">
    disabledcmds = Split(game.pane_commands)
    if (HasAttribute(game, "alwaysenabledcommands")) {
      // I'm assuming some commands, like "look", will always be available because 
      // you don't need an object to use them on
      disabledcmds = ListExclude(disabledcmds, game.alwaysenabledcommands)
    }
    foreach (obj, ScopeReachable()) {
      foreach (verb, GetDisplayVerbs(obj)) {
        if (ListContains (disabledcmds, verb)) {
          list remove (disabledcmds, verb)
          EnablePaneCommand (verb)
        }
      }
    }
    foreach (cmd, disabledcmds) {
      DisablePaneCommand (verb)
    }
  </function>

Oh, I missed that verbs were going to be in the command pane. I assumed the verbs would be left out of the command pane, since they are displayed for each object upon clicking them in the other panes.

Good catch, mrangel!


Thank you very much. I'll try these suggestions in my game.


I assumed the intention is to reverse the UI, so you click on a verb and then choose the object to apply it to.

I would note that the code I posted above is inefficient; if that's really what you want, then it's better to do it in pure javascript. Because the JS already has a list of verbs for all the objects, passed to the updateList function. So in this case, you could make updateList enable/disable verbs in the command pane each time it's updated, without having to add extra code on the Quest side.


Interesting thread this one!
I love the old SCUMM games!


I found a solution for visually disabling (i.e. graying out) or enabling buttons. It requires having fun with CSS. I followed (again) The Pixie's tutorial, but I added an id property to each span element (i.e. button), so I can simply change their style with some JS.setCss commands. After doing that, handling disabled or enabled buttons in game is simply a matter of scripting. I created a function that calculates which buttons have to be enabled or disabled (by counting objects that can be taken, opened, closed and so on) and I put a call to it into a turn script and when entering a room. Buttons get disabled or enabled automatically now.


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

Support

Forums