Complex commands

The Pixie
So you want to use THIS with THAT? what is the best way to handle it?

TIE CORD TO HOOK
CUT ROPE WITH KNIFE
ATTACK GOBLIN WITH KNIFE
GET HOT COAL WITH TONGS
IGNITE FIREWORK WITH MATCH

First, remember that GIVE and USE are already built in; if you want to use them, tick the feature on one of the items, and go to the Use/give tab.

As an example, I am going to implement TIE CORD TO HOOK.


Command Pattern

First you need a command pattern. We could do this:
tie cord to hook

If you do that then the player has to type that exact phrase or Quest will not recognise it. We can concatenate alternatives by separating them with semi-colons (and optionally spaces too).
tie cord to hook; tie thread to hook; tie string to hook

However, we can do even better, and let Quest handle the synonyms, by using #object#.
tie #object# to hook

As long as you have set up the cord with these alternative names (the Other names list on the Object tab), Quest will match #object# against any of them. An important benefit here is that your game will have the same set of synonyms for an object whether the player is trying to ick it up, look at it or tie it to a hook.

And you can have as many objects as you like in the pattern; just make sure they start object so Quest will match them against objects that are present. We need two here, the cord and the hook:
tie #object_cord# to #object_hook#

You might now want to include alternative verbs.
tie #object_cord# to #object_hook#; attach #object_cord# to #object_hook#; fasten #object_cord# to #object_hook#



Regular Expression (optional!)

If you are feeling brave, you could use a regular expression here (remember to set Regular expression from the drop down list).
^(tie|attach|fasten) (?<object_cord>.*) to (?<object_hook>.*)$

The ^ at the start says Quest must match this to the start of the command, whilst the $ at the end says this must ber the end of the command. The (tie|attach|fasten) tells Quest it has to match to one of these. (?<object_cord>.*) is equivalent to #object_cord#. Plain text, like "to", has to be matched exactly.

May be not much point in this example, but if you have variations in the joining word to handle too, you could be looking at a lot of combinations, so this way may be easier. For example:
^(get|pick up|take) (?<object_cord>.*) (using|holding|with) (the |)(?<object_hook>.*)$

Or even:
^((get|pick up|take) (the |)(?<object_cord>.*) (using|holding|with) (the |)(?<object_hook>.*)|(using|holding|with) (the |)(?<object_hook>.*) (get|pick up|take) (the |)(?<object_cord>.*))$

That will handle any of these:

get hat with hook
pick up hat using hook
take the hat with the hook
using the hook take the hat


Script

So we have a command pattern or regular expression that Quest will use to match this command, now we need to do something, so we need a script. Because we used this as the command pattern:
tie #object_cord# to #object_hook#

... Quest will already have assigned values to two special variables, in this case called object_cord and object_hook. We do not know specifically what they are, but they will be objects that are present in the current room or in the player's inventory.

The way to write the code is going to be the same for most commands. Think of a checklist; what do we need to check before allowing the command to work?

1. The player has the first object
2. The first object is the cord
3. The second object is the hook
4. The cord is not already tied to the hook

As Quest will only match an object if it is present, so we do not need to check if the hook is present, it must be if Quest found it (and you may choose not to check number 1; does the player need the cord in his inventory or should the command work if the cord is lying on the ground?).

For these four conditions, we convert them to a if/else if/else cascade, at each step testing if it is not so:
// 1. The player has the first object
if (not object_cord.parent = player) {
msg("You are not holding " + GetDisplayName(object_cord) + ".")
}
// 2. The first object is the cord
else if (not object_cord = cord) {
msg("You cannot tie the " + GetDisplayName(object_cord) + " to anytthing.")
}
// 3. The second object is the hook
else if (not object_hook = hook) {
msg("You cannot tie anything to the " + GetDisplayName(object_hook) + ".")
}
// 4. The cord is not already tied to the hook
else if (GetBoolean(object_cord, "tiedtohook")) {
msg("You've already done that.")
}
// Everything checked, so do it
else {
msg("You tie the cord to the hook.")
cord.tiedtohook = true
code.take = false
cord.parent = player.parent
}



More General

Suppose there are several objects the cord might be tied to, what is the best way to handle that? What we want is a command that can handle tying the cord to any such object, so the first thing to do is to flag an object as attachable. Go to the Attributes tab of each object, and add a new attribute, attachable, set it to be a Boolean, and tick it. Now our command can check if the object has that set, and if it does, the cord can be tied to it.

The code here has two changes. Condition number 3 now checks the attachable flag, instead of checking the object in the hook. Also, at the end, an attribute on the cord gets set to the object it is attached to, so you can test what that was if necessary.
// 1. The player has the first object
if (not object_cord.parent = player) {
msg("You are not holding " + GetDisplayName(object_cord) + ".")
}
// 2. The first object is the cord
else if (not object_cord = cord) {
msg("You cannot tie the " + GetDisplayName(object_cord) + " to anytthing.")
}
// 3. The second object is the attachable
else if (not GetBoolean(object_hook, "attachable")) {
msg("You cannot tie anything to the " + GetDisplayName(object_hook) + ".")
}
// 4. The cord is not already tied to the hook
else if (GetBoolean(object_cord, "tiedtohook")) {
msg("You've already done that.")
}
// Everything checked, so do it
else {
msg("You tie the cord to the hook.")
cord.tiedtohook = true
code.take = false
cord.parent = player.parent
cord.attachedto = object_hook
}

Alex
You can also do this using two-object verbs: http://blog.textadventures.co.uk/2012/0 ... quest-5-2/

XanMag
You can make fun of me all you want behind my back, but for now, just tolerate me... :oops:

Scenario:

I would like to add a command that will handle essentially this, "tell children to shut up".

In the command box, I would like to avoid typing all of the synonyms for children (which is the name of my object) in the command bar.

How I currently handle this: tell children to shut up; tell kids to shut up; tell kid to shut up; tell child to shut up; ask kids to be quiet; ask children to be quiet; ask kid to be quiet; ask child to be quiet; etc. Tedious and unnecessary I believe.

So, What I would like to do - something like this: tell #object# to shut up; ask #object# to be quiet; etc...

If I do that, how do I handle the response? I assume I would somehow do something like this: If #object# = children then print appropriate response, but I have no real idea how to do that without the GUI. I do have 'kids', 'kid', 'child' as 'other names' for children.

Thanks is advance.

The Pixie
Here is a regular expression you can use:

^(tell|ask) (?<object>.*) to (be quiet|keep quiet|shut up|hush|be silent)$

In your code:
if (not object = children) {
msg("You tell the " + GetDisplayName(object) + " to be quiet. Not that it was talking much anyway.")
}
else {
msg("You tell the children to keep quiet. stuff happens.")
}

XanMag
I have used this and it seems to be working great. I would however, like to give a different response for the other living person in the room that is not "children". I tried to add an else if (object = woman) then print message but it only gave the then command under not children.

To solve this, do I need to put this as a separate 'if' above the script you provided? (I'd try it, but I am not home at the moment)

Thanks again!

The Pixie
I would do it like this:
if (object = children) {
msg("You tell the children to keep quiet. stuff happens.")
}
else if (object = woman) {
msg("'Quiet woman!' you say.")
}
else {
msg("You tell the " + GetDisplayName(object) + " to be quiet. Not that it was talking much anyway.")
}

XanMag
Got the above working just fine. Thanks! But...

Just testing some stuff out here for my tutorial game. What is wrong with this code?

^(cover|fill) (the |) (?<object_hole>.*) (with|into|over|onto) (the |) (?<object_dirt pile>.*)$

I get the following error - Error running script: Error evaluating expression 'IsRegexMatch(cmd.pattern, command, cmd.name)': parsing "^(cover|fill) (the |) (?<object_hole>.*) (with|into|over|onto) (the |) (?<object_dirt pile>.*)$" - Invalid group name: Group names must begin with a word character.

...and that happens as soon as I type anything.

Whereas, this regular command expression gives the results I want. ^(tell|ask) (?<object>.*) to (be quiet|keep quiet|shut up|hush|be silent)$

I assumed it would equate cover and fill to the hole with/into/onto/over dirt pile.

Thanks!

The Pixie
I would guess it does not like the space in:
(?<object_dirt pile>.*)
The difference is that the underlying code will try to convert "object_dirt pile" to the name of a variable. While Quest itself does not mind spaces in variables, the underlying code does.

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

Support

Forums