-
Notifications
You must be signed in to change notification settings - Fork 17
Command Actions
An approach to modifying commands is to build a list of exceptions. In this methodology, to create locations and items using just the standard attributes - essentially just exits and descriptions - and give items templates as appropriate. Separate to that, you create a list of command actions that can intercept a command in a specific situation to do something different. This is something like Specific Tasks in ADRIFT.
For example, one item could be a priceless vase. Dropping the item will break it, and we could handle that with a command action that is applied for the "Drop" command and the vase object.
Whether this is a good way to do it is debatable. My feeling is that you are probably better keeping the breaking of the vase with the vase item. However, this approach could be useful in an editor.
Also, it does have other uses that would be difficult to do other ways. Many of the examples below are for intercepting commands when the player is in limbo; when in that state the player cannot examine anything, and we can use a command action to handle that.
Command actions require a command name, a script and some parameters to determine when to use it.
Here is a simple example that just tests the player state. If player.inLimbo
is true
, the "test" function returns true and this action will be used, i.e., the "script" function will be run.
new CmdAction({
name:'Examine',
test:function() {
return player.inLimbo
},
script:function() {
msg("You cannot see, feel or hear anything...")
return world.ABORT_FAILED
},
})
The function returns a special value that tells Quest the command failed, and not to bother with any further processing. Further processing means going through other items. If the player did EXAMINE HAT, BALL (and player.inLimbo
is true
), Quest will do the hat first, get to this action command, and then stop without bothering to do the ball.
Usually, your action will be specific to one item. This example only applies to the camera.
new CmdAction({
name:'Examine',
test:function(item) {
return item.name === 'camera'
},
script:function(item, objects, multiple) {
msg(multiple ? "Camera: Weird..." : "Weird...")
return world.SUCCESS
},
})
Both "test" and "script" are passed the item, the other objects for the command (see here), and a flag to indicate if this is one of several items. That flag is used in this example to change the message.
In fact, testing for an item is so common there is a short cut. Note that it uses the name of the item, not the item itself.
new CmdAction({
name:'Examine',
item:'camera',
script:function(item, objects, multiple) {
msg(multiple ? "Camera: Weird..." : "Weird...")
return world.SUCCESS
},
})
You can also use "items", together with a list of item names, and the action will only apply to an item in that list; or "clone", and it will apply only to clones of the named item (and not the item itself).
You should only use one of "item", "items" and "clones", but it will often be useful to use one of them together with "test". In this example, any clone of the chrono-seed will be glowing if examined while in limbo.
new CmdAction({
name:'Examine',
clone:'chrono_seed',
test:function() {
return player.inLimbo
},
script:function(item, objects, multiple) {
msg(multiple ? "Strange seed: It's... glowing." : "It's... glowing.")
return world.SUCCESS
},
})
This example sets a flag on the item in the "script", while the "test" checks the flag is not set. This means it will only happen once.
new CmdAction({
name:'Examine',
item:'magic_ring',
test:function() {
return !magic_ring.inscriptionSeen
},
script:function(item, objects, multiple) {
msg("You look at the curious ring, and for a moment think there is writing on the inside, but then it is gone. Probably just a trick of the light.")
magic_ring.inscriptionSeen = true
return world.SUCCESS
},
})
You can have as many command actions as you like, so what happens if more than one applies? Very simply, Quest will use the first it finds, and only that one.
This means you need to put them in the file in a suitable order. I would suggest having all command actions in one file, and grouping them by command, and within each command grouping by item.
If you do not know the name of a command, you can look it up in lang/lang-en.js. The patterns for all the built-in commands are at the top of the file, like this:
Examine:/^(?:examine|exam|ex|x|describe) (.+)$/,
The command name is the bit before the colon, in this case "Examine". You need to get the case right.
Some commands have no items.
SIT and STAND, for example, are just used on their own. This system works fine; obviously the "item", "items" and "clone" attributes make no sense here.
Some commands have parameters that are not items. The SAY command, for example, captures the text, rather than an item in the objects
list. To get a command action to work with this, you will need to flag it as "ignoreItems".
Note that in this case world.ABORT_FAILED
is used to prevent the normal command running as well, rather than to skip other objects.
new CmdAction({
name:'Say',
ignoreItems:true,
test:function(item, objects) {
return player.inLimbo
},
script:function(objects) {
const text = objects[1]
msg("You try to say '" + text + "', but no words come out of your mouth.")
return world.ABORT_FAILED
},
})
The SAY command also handles SHOUT, YELL, etc. and the first entry in the objects
list is the verb, which is why objects[1]
. This brings us to a couple of points to be aware of.
Some one-word commands use a generic command called "OneWord". Currently these are LISTEN, SMELL (and SNIFF), DANCE, SING and PRAY. These would need to be handled like SAY, remembering that you need to check that you have the right one.
new CmdAction({
name:'OneWord',
ignoreItems:true,
test:function(objects) {
return player.inLimbo && objects[0] === 'pray'
},
script:function(objects) {
msg("You pray, wondering if the gods can hear you when you are here.")
return world.ABORT_FAILED
},
})
Note that the "test" and "script" functions do not use a item parameter, as that makes no sense in this context.
If you have your own commands, you can use command actions with them too. The only issue to be aware of is that if you command uses items, then the primary items must be first in the objects
list. Usually that is the case any way, and you are good to go.
The "Clean" command is example where that is not the case, as it also captures the verb, in case the author wants to handle "DUST" differently to "RUB". The trick here is to flag the regex as reversed.
new Cmd('Clean', {
regexes:[{regex:/^(clean|rub|dust|polish|shine) (.+)$/, mod:{reverse:true}}],
objects:[
{scope:parser.isPresent},
{special:'text'},
],
defmsg:lang.cannot_clean,
})
Tutorial
- First steps
- Rooms and Exits
- Items
- Templates
- Items and rooms again
- More items
- Locks
- Commands
- Complex mechanisms
- Uploading
QuestJS Basics
- General
- Settings
- Attributes for items
- Attributes for rooms
- Attributes for exits
- Naming Items and Rooms
- Restrictions, Messages and Reactions
- Creating objects on the fly
- String Functions
- Random Functions
- Array/List Functions
- The
respond
function - Other Functions
The Text Processor
Commands
- Introduction
- Basic commands (from the tutorial)
- Complex commands
- Example of creating a command (implementing SHOOT GUN AT HENRY)
- More on commands
- Shortcut for commands
- Modifying existing commands
- Custom parser types
- Note on command results
- Meta-Commands
- Neutral language (including alternatives to "you")
- The parser
- Command matching
- Vari-verbs (for verbs that are almost synonyms)
Templates for Items
- Introduction
- Takeable
- Openable
- Container and surface
- Locks and keys
- Wearable
- Furniture
- Button and Switch
- Readable
- Edible
- Vessel (handling liquids)
- Components
- Countable
- Consultable
- Rope
- Construction
- Backscene (walls, etc.)
- Merchandise (including how to create a shop)
- Shiftable (can be pushed from one room to another)
See also:
- Custom templates (and alternatives)
Handing NPCs
- Introduction
- Attributes
- Allowing the player to give commands
- Conversations
- Simple TALK TO
- SAY
- ASK and TELL
- Dynamic conversations with TALK TO
- TALK and DISCUSS
- Following an agenda
- Reactions
- Giving
- Followers
- Visibility
- Changing the player point-of-view
The User Experience (UI)
The main screen
- Basics
- Printing Text Functions
- Special Text Effects
- Output effects (including pausing)
- Hyperlinks
- User Input
The Side Panes
Multi-media (sounds, images, maps, etc.)
- Images
- Sounds
- Youtube Video (Contribution by KV)
- Adding a map
- Node-based maps
- Image-based maps
- Hex maps
- Adding a playing board
- Roulette!... in a grid
Dialogue boxes
- Character Creation
- Other example dialogs [See also "User Input"]
Other Elements
- Toolbar (status bar across the top)
- Custom UI Elements
Role-playing Games
- Introduction
- Getting started
- Items
- Characters (and Monsters!)
- Spawning Monsters and Items)
- Systema Naturae
- Who, When and How NPCs Attack
- Attributes for characters
- Attacking and guarding
- Communicating monsters
- Skills and Spells
- Limiting Magic
- Effects
- The Attack Object
- [Extra utility functions](https://github.com/ThePix/QuestJS/wiki/RPG-Library-%E2%80%90-Extra Functions)
- Randomly Generated Dungeon
- Quests for Quest
- User Interface
Web Basics
- HTML (the basic elements of a web page)
- CSS (how to style web pages)
- SVG (scalable vector graphics)
- Colours
- JavaScript
- Regular Expressions
How-to
Time
- Events (and Turnscripts)
- Date and Time (including custom calendars)
- Timed Events (i.e., real time, not game time)
Items
- Phone a Friend
- Using the USE verb
- Display Verbs
- Change Listeners
- Ensembles (grouping items)
- How to spit
Locations
- Large, open areas
- Region,s with sky, walls, etc.
- Dynamic Room Descriptions
- Transit system (lifts/elevators, buses, trains, simple vehicles)
- Rooms split into multiple locations
- Create rooms on the fly
- Handling weather
Exits
- Alternative Directions (eg, port and starboard)
- Destinations, Not Directions
Meta
- Customise Help
- Provide hints
- Include Achievements
- Add comments to your code
-
End The Game (
io.finish
)
Meta: About The Whole Game
- Translate from Quest 5
- Authoring Several Games at Once
- Chaining Several Games Together
- Competition Entry
- Walk-throughs
- Unit testing
- Debugging (trouble-shooting)
Releasing Your Game
Reference
- The Language File
- List of settings
- Scope
- The Output Queue
- Security
- Implementation notes (initialisation order, data structures)
- Files
- Code guidelines
- Save/load
- UNDO
- The editor
- The Cloak of Darkness
- Versions
- Quest 6 or QuestJS
- The other Folders
- Choose your own adventure