Lich:Script foreach: Difference between revisions
No edit summary |
(additional documentation) |
||
| (7 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
{{3rd-party}} |
|||
=== What is it? === |
=== What is it? === |
||
foreach is a script for sending a series of commands for each item that matches its criteria. |
|||
While not a full replacement for "proper" purpose-built Lich scripts, it is still frequently useful in its own regard. |
While not a full replacement for "proper" purpose-built Lich scripts, it is still frequently useful in its own regard. Anywhere you would otherwise repeat the same handful of commands once per item -- moving boxes, cutting gems, reading scrolls, selling for a bounty -- foreach can do it in a single line. |
||
=== I don't want to read docs, can you give me some examples? |
=== I don't want to read docs, can you give me some examples? === |
||
No rogues around? Move all your boxes to your locker: |
No rogues around? Move all your boxes to your locker: |
||
`;foreach box in inv; move to locker` |
`;foreach box in inv; move to locker` |
||
Move all your boxes back out of your locker: |
|||
Own a gemcutter and want to cut all the gems in a container? |
|||
`;foreach box in locker; move to backpack` |
|||
Own a gemcutter and want to cut all the gems in a container? |
|||
`;foreach gem in red sack; move to gemcutter; turn gemcutter; waitrt; move to blue sack` |
`;foreach gem in red sack; move to gemcutter; turn gemcutter; waitrt; move to blue sack` |
||
Getting boxes picked? |
Getting boxes picked? |
||
`;foreach box in inv; get item;give item to Rogue;waitfor Rogue offers you;accept item` |
`;foreach box in inv; get item; give item to Rogue; waitfor Rogue offers you; accept item` |
||
*(This'll only work if you're fast enough to empty boxes while the rogue is picking)* |
*(This'll only work if you're fast enough to empty boxes while the rogue is picking)* |
||
Checking for orb gems? |
Checking for orb gems? |
||
Sorcerer: `;foreach gem in queue bag;get item;waitmana 4;prep 704;cast item;pause;put item in reject bag` |
Sorcerer: `;foreach gem in queue bag; get item; waitmana 4; prep 704; cast item; pause; put item in reject bag` |
||
Bard using `;loresing`: `;foreach gem in queue bag;get item;waitfor You remove;waitmana 20;;loresing purpose noun;pause;waitrt?;put item in reject bag` |
Bard using `;loresing`: `;foreach gem in queue bag; get item; waitfor You remove; waitmana 20; ;loresing purpose noun; pause; waitrt?; put item in reject bag` |
||
While the script is paused, put the gem in the orb bag if it's an orb. `;unpause` |
While the script is paused, put the gem in the orb bag if it's an orb. `;unpause` will put it in the reject bag if you're still holding it. |
||
Bulk reading scrolls? |
Bulk reading scrolls? |
||
`;foreach scroll in inv; read item` |
`;foreach scroll in inv; read item` |
||
Appraise every gem in a container (foreach gets each one, appraises it, and puts it back): |
|||
Accidentally move a bunch of items to the wrong container using foreach? |
|||
`;foreach in |
`;foreach gem in backpack; appraise` |
||
*or* `;foreach in last;get item;move to correct backpack` |
|||
Turning in gems or herbs for a bounty? |
|||
`;foreach name=uncut diamond in inv;get item;sell item` |
|||
Don't want to sell ALL of your diamonds for said bounty? |
|||
`;foreach first 7 name=uncut diamond in inv;get item;sell item` |
|||
Doing a long task and want to be able to resume it later (possibly on the same source container?) |
|||
`;foreach unmarked gem in whereever;get item;do lots of things;mark item;return` as many times as needed, followed by |
|||
`;foreach marked gem in whereever;get item;unmark item` |
|||
(`whereever` must be a container in your inventory or your entire `inv`) |
|||
Accidentally move a bunch of items to the wrong container using foreach? |
|||
Need to ditch your sorcerer wands but want the wizard ones? |
|||
`;foreach in last; move to container` *(back where they started)* |
|||
`;foreach q=/(bloodwood|twisted|yew|bone|glass|thanot) wand/ in inv;sell` |
|||
*or* `;foreach in last; get item; move to correct backpack` |
|||
(This uses the new pattern matching introduced in 0.10.5) |
|||
Turning in gems or herbs for a bounty? |
|||
`;foreach name=uncut diamond in inv; get item; sell item` |
|||
=== That's nice. But what can it REALLY do? === |
|||
Don't want to sell ALL of your diamonds for said bounty? |
|||
== Usage == |
|||
`;foreach first 7 name=uncut diamond in inv; get item; sell item` |
|||
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**;** _command1_**;** _command2_**;** ...] |
|||
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**/** _command1_**/** _command2_**/** ...] |
|||
Sell everything in a container that the local pawnshop will buy: |
|||
`;foreach sellable=pawnshop in my cloak; get item; sell item` |
|||
Doing a long task and want to be able to resume it later (possibly on the same source container)? |
|||
`;foreach unmarked gem in wherever; get item; do lots of things; mark item; return` as many times as needed, followed by |
|||
`;foreach marked gem in wherever; get item; unmark item` |
|||
(`wherever` must be a container in your inventory or your entire `inv`) |
|||
Need to ditch your sorcerer wands but want to keep the wizard ones? |
|||
`;foreach q=/(bloodwood|twisted|yew|bone|glass|thanot) wand/ in inv; sell` |
|||
(This uses regular-expression pattern matching -- note the slashes.) |
|||
Match a specific full name (article and adjectives included): |
|||
`;foreach name=*quartz orb in inv; get item; put item in locker` |
|||
=== That's nice. But what can it REALLY do? === |
|||
== Usage == |
|||
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**;** _command1_**;** _command2_**;** ...] |
|||
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**/** _command1_**/** _command2_**/** ...] |
|||
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**|** _command1_**|** _command2_**|** ...] |
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**|** _command1_**|** _command2_**|** ...] |
||
Typing `;foreach` or `;foreach help` by itself prints the built-in help, including the live list of item types and sellable categories that Lich currently knows about. |
|||
=== Options === |
|||
== Options == |
|||
_options_ is a set of optional filters on what matching items `foreach` will operate on. It can consist of zero or more |
_options_ is a set of optional filters on what matching items `foreach` will operate on. It can consist of zero or more |
||
of the following: |
of the following: |
||
**registered** _or_ **unregistered** |
**registered** _or_ **unregistered** |
||
Only |
Only matches items that are registered or not registered. This option is only available if the items in question |
||
are in your inventory or your premium locker. (`locker manifest` is used to determine item status in the latter case, |
are in your inventory or your premium locker. (`locker manifest` is used to determine item status in the latter case, |
||
which is not available for standard lockers.) |
which is not available for standard lockers.) |
||
**marked** _or_ **unmarked** |
**marked** _or_ **unmarked** |
||
Only |
Only matches items that are marked or not marked as unsellable. This has the same restrictions as the above. |
||
You can use this in conjunction with MARKing items as |
You can use this in conjunction with MARKing items as part of your ;foreach script to prevent operating on the same |
||
item more than once in situations where you might run ;foreach multiple times. |
item more than once in situations where you might run ;foreach multiple times. |
||
**unique** |
|||
**unique** |
|||
Only the first item with any given full name will be matched. This is global between all containers, so if you have |
|||
Only the first item with any given full name will be matched. This is global across all containers, so if you have |
|||
the same item in two different containers only the first item (whichever `foreach` sees first) will be matched. |
the same item in two different containers only the first item (whichever `foreach` sees first) will be matched. |
||
**first _N_** |
|||
**first _N_** |
|||
Stop after _N_ matching items have been encountered. (The word `first` is optional) |
|||
Stop after _N_ matching items have been encountered. (The word `first` is optional, so `;foreach 5 gem ...` works.) |
|||
**skip _N_** _or_ **after _N_** |
|||
**skip _N_** _or_ **after _N_** |
|||
Skip the first _N_ matching items. |
Skip the first _N_ matching items. |
||
**sort** or **sorted** |
|||
**sort** _or_ **sorted** |
|||
Sort items by name within each respective container. Articles "a", "an", "some" and "the" are ignored when sorting. |
|||
Sort items by full name within each respective container. Articles "a", "an", "some" and "the" are ignored when |
|||
Containers will still be handled one at a time, and the order of the containers themselves is not affected. |
|||
sorting. Containers are still handled one at a time, and the order of the containers themselves is not affected. |
|||
**reverse** or **reversed** |
|||
Reverse the order of items within each container. If used alone, this means the last item in each container in |
|||
**nounsort** _or_ **nsort** _(also_ **nsorted** _/_ **nounsorted**_)_ |
|||
processed first and vice versa. If used with **sorted**, items will be handled in descending alphabetical order |
|||
Sort items by noun first, then by full name, within each respective container. Useful when you want all of one kind |
|||
rather than ascending. |
|||
of item grouped together regardless of adjectives. As with **sort**, articles are ignored and container order is |
|||
Containers will still be handled one at a time, and the order of the containers themselves is not affected. |
|||
unaffected. (**sort** and **nounsort** are mutually exclusive -- specify at most one.) |
|||
Options are applied roughly in the order listed above. You can combine **first** with **skip**: |
|||
**reverse** _or_ **reversed** |
|||
Reverse the order of items within each container. If used alone, this means the last item in each container is |
|||
processed first and vice versa. If used with **sorted** (or **nounsort**), items are handled in descending order |
|||
rather than ascending. Containers are still handled one at a time, and the order of the containers themselves is not |
|||
affected. |
|||
Options are applied roughly in the order listed above. You can combine **first** with **skip**: |
|||
`;foreach first 5 after 10` will match items 11 through 15. |
`;foreach first 5 after 10` will match items 11 through 15. |
||
== What == |
|||
_what_ allows you to filter the types of items `foreach` acts on. It can be one of the following |
_what_ allows you to filter the types of items `foreach` acts on. It can be one of the following: |
||
**type=**_pattern_ or **t=**_pattern_ or simply _pattern_ |
**type=**_pattern_ or **t=**_pattern_ or simply _pattern_ |
||
You can specify any type of item that Lich knows about. Examples include 'gem', 'wand' and 'scroll' |
You can specify any type of item that Lich knows about. Examples include 'gem', 'wand' and 'scroll' |
||
(which includes things like |
(which includes things like palimpsests and other 'alternate names' for scrolls). `;foreach` by itself will show |
||
the known types. `;foreach in inv` with no commands will show the types of all items it finds. |
all the known types. `;foreach in inv` with no commands will show the types of all items it finds. |
||
You can use `type=none` to explicitly find items that have no defined type according to Lich. |
|||
**Note:** This is only as accurate as Lich's own type data. You may be able to get more accurate type data by downloading and running the `gameobjadd` script at startup. |
|||
You can use `type=none` (or `type=unknown`) to explicitly find items that have no defined type according to Lich. |
|||
**noun=**_pattern_ or **n=**_pattern_ |
|||
**Note:** This is only as accurate as Lich's own type data. You may be able to get more accurate type data by |
|||
downloading and running the `gameobjadd` script at startup, or by keeping `gameobj-data.xml` up to date. |
|||
**sellable=**_pattern_ or **s=**_pattern_ |
|||
You can match items by which shops will buy them, based on Lich's sellable data. For example, |
|||
`;foreach sellable=pawnshop in my cloak; get item; sell item` will sell everything the pawnshop accepts. |
|||
Use `sellable=none` (or `sellable=unknown`) to match items with no known sellable category. |
|||
`;foreach` by itself lists every sellable category Lich currently knows about. |
|||
**noun=**_pattern_ or **n=**_pattern_ |
|||
You can specify items that have a specific noun. This must be an exact match (unless using wildcards). |
You can specify items that have a specific noun. This must be an exact match (unless using wildcards). |
||
**name=**_pattern_ or **m=**_pattern_ |
**name=**_pattern_ or **m=**_pattern_ |
||
You can specify items that have a specific name. |
You can specify items that have a specific name. The name is the short display name without the long description -- |
||
for example `uncut diamond`, `enruned mithril chest`, or `old scroll`. Matching is anchored, so the whole name must |
|||
match unless you use wildcards (`name=*diamond`). |
|||
**fullname=**_pattern_ or **f=**_pattern_ |
|||
You can specify items that have a specific full name. This is like `name=pattern`, but includes things like long |
|||
**fullname=**_pattern_ or **f=**_pattern_ |
|||
You can specify items that have a specific full name. This is like `name=pattern`, but includes things like long |
|||
descriptions. See `;foreach in inv` to get a list of item full names. |
descriptions. See `;foreach in inv` to get a list of item full names. |
||
**quick=**_pattern_ or **q=**_pattern_ |
**quick=**_pattern_ or **q=**_pattern_ |
||
Identical to `fullname`, except the ends of |
Identical to `fullname`, except the ends of _pattern_ are automatically wildcarded. |
||
`;foreach q=red,blue ...` is equivalent to `;foreach f=*red*,*blue* ...` |
`;foreach q=red,blue ...` is equivalent to `;foreach f=*red*,*blue* ...` |
||
(When used with a regular expression, `quick` behaves exactly like `fullname`.) |
|||
_pattern_ can be: |
_pattern_ can be: |
||
`one or more words |
`one or more words including spaces`: Must be an exact match (subject to the anchoring rules of the chosen attribute). |
||
`*use*asterisks*for*wildcards`: Asterisks function as wildcards. `name=*snowflake-cut*` will find anything with |
`*use*asterisks*for*wildcards`: Asterisks function as wildcards. `name=*snowflake-cut*` will find anything with |
||
'snowflake-cut' anywhere in the name, for instance. |
'snowflake-cut' anywhere in the name, for instance. |
||
`this,that`: Match any one of the options separated by commas. |
`this,that`: Match any one of the options separated by commas. |
||
`blue sapphire,emerald,*diamond,*emerald,uncut ruby`: You can mix the above. |
`blue sapphire,emerald,*diamond,*emerald,uncut ruby`: You can mix the above. |
||
`/(blue|white) crystal/`: You can match against a regular expression. The slashes are required, and matching is |
`/(blue|white) crystal/`: You can match against a regular expression. The slashes are required, and matching is |
||
always case-insensitive. |
always case-insensitive. (Inline regex flags after the closing slash are not supported.) |
||
All attribute matching is case-insensitive. |
|||
=== Targets === |
|||
You can also use **ALL**, **ANY** or **EVERYTHING** as the _what_ value to deliberately match every item. This is |
|||
required as a safety confirmation when running commands against your entire `inv` (see Targets, below). |
|||
== Targets == |
|||
_targets_ tells ;foreach where you want to look for the items. You can specify one or more _target_, separated by commas. |
_targets_ tells ;foreach where you want to look for the items. You can specify one or more _target_, separated by commas. |
||
| Line 126: | Line 167: | ||
Each target can be the name of an actual container in game ("cloak", "my backpack", etc.), or one of the following options: |
Each target can be the name of an actual container in game ("cloak", "my backpack", etc.), or one of the following options: |
||
**inv** or **inventory** |
**inv** or **inventory** |
||
Examines the contents all containers in your inventory. Note that containers in your hands are not in your |
Examines the contents of all containers in your inventory. Note that containers in your hands are not in your |
||
inventory. As a safety measure, `;foreach in inv` with commands but no _what_ filter will refuse to run unless you |
|||
explicitly say `;foreach ALL in inv; ...` (so you don't accidentally act on everything you own). |
|||
**floor** or **ground** or **room** |
|||
Examines items on the ground. *The items, not their contents* |
|||
**worn** |
|||
Examines items in your inventory, *not* their contents. Want to register everything you are wearing? |
|||
**loot** |
|||
`;foreach in worn; remove item; register item; wear item` |
|||
Examines the contents of containers that are on the ground -- boxes, disks, errantly left behind backpacks, etc. |
|||
**floor** or **ground** or **room** |
|||
**locker** |
|||
Examines |
Examines items on the ground. *The items, not their contents.* |
||
** |
**loot** |
||
Examines the contents of containers that are on the ground -- boxes, disks, errantly left-behind backpacks, etc. |
|||
Examines items in your inventory, *not* their contents. Want to register everything you are wearing? |
|||
`foreach in worn; remove item;register item;wear item` |
|||
**desc** |
|||
Examines items mentioned in the room description (things that are part of the room rather than loose loot). |
|||
**last** or **previous** |
|||
Like `floor`/`loot`, this cannot be filtered by marked/registered status. |
|||
Last is a set of items that matches whatever the last run of foreach found. You can use this to quickly undo if you |
|||
manage to flub something up, like accidentally moving all of your inventory to your locker rather than just your boxes. |
|||
**locker** |
|||
Examines the contents of your locker. *If your locker is not open, ;foreach will open it and close it when it is |
|||
When using **last**, the container items are reported in will be the container they started the previous run in -- not |
|||
done.* On premium lockers it uses your `locker manifest` for a fast scan and to determine marked/registered status; |
|||
their current location. So `;foreach in last;move to container` will move all (reachable) items to their original |
|||
if that fails it falls back to a slower legacy scan. |
|||
locations. |
|||
** |
**last** or **previous** |
||
Last is the set of items that matched whatever the previous run of foreach found. You can use this to quickly undo |
|||
Similar to **inv**, but this uses container contents as currently known by Lich rather than doing an INV FULL. This |
|||
things, like accidentally moving all of your inventory to your locker rather than just your boxes. |
|||
When using **last**, items are reported in the container they started the previous run in -- not their current |
|||
location. So `;foreach in last; move to container` will move all (reachable) items back to their original locations. |
|||
(Filtering **last** by marked/registered status is not supported.) |
|||
**fastinv** or **qinv** |
|||
Similar to **inv**, but this uses container contents as currently known by Lich rather than doing an INV FULL. This |
|||
allows for faster startup, but with two caveats: |
allows for faster startup, but with two caveats: |
||
- Item registration/marked status is unavailable. (If you're using one of the relevant _options_, this will be |
- Item registration/marked status is unavailable. (If you're using one of the relevant _options_, this will be |
||
automatically converted to `inv`) |
automatically converted to `inv`.) |
||
- Containers that Lich doesn't know the contents of will be skipped. Most notably, if you have not LOOKed in a |
- Containers that Lich doesn't know the contents of will be skipped. Most notably, if you have not LOOKed in a |
||
container since you |
container since you logged in, that container will be skipped. Note that `;foreach in inv` does NOT satisfy the |
||
"looked in a container" requirement, but `;foreach in worn; look in item` probably will. |
|||
If you specified a _what_ (above section), it will further restrict the list of what foreach is working on. |
If you specified a _what_ (above section), it will further restrict the list of what foreach is working on. |
||
=== Commands === |
|||
**Container name suffix:** You can append a question mark to a named container so that foreach skips it gracefully |
|||
Commands are separated by semicolons (`;`), slashes (`/`) or pipes (`|`). You can only use type of separator in a given |
|||
instead of failing if it is closed or missing. For example, `;foreach box in inv,disk?; move to locker` will still |
|||
invocation of Foreach -- it's determined by the first one Foreach sees. The examples here all use semicolons, but the other two formats may be useful if one of your commands needs to |
|||
work even if you have no disk. |
|||
actually include semicolons (aside from Lich scripts) -- say, the lines of `LORESING`. |
|||
**Collective containers:** If LOOKing at a target reports a group -- for example "You see three mannequins. Looking at |
|||
If you don't give Foreach any commands to perform, it simply spits out a list of all the matching items and some |
|||
the mannequins, you see a dark grey mannequin, ..." -- foreach will operate on every container in that list. So |
|||
relevant stats. This is useful, but usually you want to do something with items. |
|||
`;foreach on mannequins; ...` handles each mannequin in turn. |
|||
**Position keyword:** `in`, `on`, `under` and `behind` control how foreach LOOKs at named containers. Most containers |
|||
Any command you can send to the game can be used with Foreach. There's also a few shortcuts, conveniences, and some |
|||
use `in`; use `on` for surfaces (tables, racks), and `under`/`behind` where appropriate. |
|||
very basic scripting support (like waiting for a piece of text) |
|||
== Commands == |
|||
For commands to be worthwhile, there needs to be an option to replace some part of the command with information about |
|||
the item. For this, there's some variable replacement rules -- as noted in the list below: |
|||
Commands are separated by semicolons (`;`), slashes (`/`) or pipes (`|`). You can only use one type of separator in a |
|||
=== Replacements === |
|||
given invocation of Foreach -- it's determined by the first one Foreach sees. The examples here all use semicolons, but |
|||
the other two formats are useful if one of your commands needs to include semicolons itself (for example, when invoking a |
|||
Lich script, or the lines of `LORESING`). |
|||
If you don't give Foreach any commands to perform, it simply spits out a list of all the matching items and some relevant |
|||
stats (including item type, which makes `;foreach in backpack` a quick way to discover what `type=` value to use). This |
|||
is useful, but usually you want to do something with items. |
|||
Any command you can send to the game can be used with Foreach. You can also invoke other Lich scripts by including the |
|||
leading `;` (for example `;foreach gem in backpack; get item; ;my_script item; put item in container`). There are also a |
|||
few shortcuts, conveniences, and some very basic scripting support (like waiting for a piece of text). |
|||
For commands to be worthwhile, there needs to be a way to refer to the current item. For this, there's some variable |
|||
replacement rules -- as noted in the next section. |
|||
== Replacements == |
|||
Certain words within a command will be replaced with some detail about the current item. Here's a list. |
Certain words within a command will be replaced with some detail about the current item. Here's a list. |
||
**item** |
**item** |
||
The most important replacement. This is replaced with an exact reference to an item using its unique ID. It's good |
The most important replacement. This is replaced with an exact reference to an item using its unique ID. It's good |
||
for commands, but not particularly useful if you want to talk about the item in SAY or ECHO |
for commands, but not particularly useful if you want to talk about the item in SAY or ECHO. |
||
**container** |
**container** |
||
This is like `item`, but refers to the container the item was found in. It won't work properly if the item was not |
This is like `item`, but refers to the container the item was found in. It won't work properly if the item was not |
||
in a container (e.g. when using the `floor` |
in a container (e.g. when using the `floor`, `worn` or `desc` targets). |
||
If you're using the `last` target, `container` refers to the container the item was *originally* in. Thus, the |
If you're using the `last` target, `container` refers to the container the item was *originally* in. Thus, the |
||
following will dump your inventory into your locker and then put it all back |
following will dump your inventory into your locker and then put it all back (aside from container orders becoming |
||
flipped): |
|||
`;foreach in inv;move to locker` |
`;foreach in inv; move to locker` |
||
`;foreach in |
`;foreach in last; move to container` |
||
**noun** |
**noun** |
||
This will be replaced with the item noun, e.g. `diamond`, `chest`, `scroll`. |
This will be replaced with the item noun, e.g. `diamond`, `chest`, `scroll`. |
||
**name** |
**name** |
||
This will be replaced with the item name, e.g. `uncut diamond`, `enruned mithril chest`, `old scroll`. |
This will be replaced with the item name, e.g. `uncut diamond`, `enruned mithril chest`, `old scroll`. |
||
== Shortcuts == |
|||
For some normal game commands, typing `command` by itself is equivalent to `command item`. Also, if one of these commands |
|||
For some normal game commands, typing `command` by itself is equivalent to `command item`. Also, if one of these |
|||
is the first command in the list, an implicit `get item` might be automatically added provided that the command refers to |
|||
commands is the first command in the list, an implicit `get item` (or `remove item`, for worn targets) may be added |
|||
the item in some way. These commands currently are: **drop**, **place**, **sell**, **put**, **appraise**, **read**, |
|||
automatically when the command needs the item in hand. These commands currently are: **drop**, **place**, **sell**, |
|||
**look**, **inspect** and **analyze** |
|||
**appraise**, **register**, **mark**, **unmark**, **get**, **take**, **read**, **look**, **inspect**, **analyze**, and |
|||
**trash**. |
|||
When the *only* command is one that does not dispose of the item -- `appraise`, `register`, `mark` or `unmark` -- an |
|||
implicit `return` is also added at the end, so the item is put back where it came from. (Commands that get rid of the |
|||
item, such as `drop`, `sell`, `trash`, `place` and `giveitem`, do not get an implicit return.) |
|||
In addition, the following convenience shortcuts exist: |
In addition, the following convenience shortcuts exist: |
||
**move** _[what]_ **to** _where_ |
**move** _[what]_ **to** _where_ |
||
Shorthand for getting _what_ and then putting it in _where_. If you omit _what_, 'item' is assumed. |
Shorthand for getting _what_ and then putting it in _where_. If you omit _what_, 'item' is assumed. _where_ can be a |
||
your locker, `;foreach` tries to open it (once) and, |
container, or `ground`/`floor` to drop the item. If _where_ is your locker, `;foreach` tries to open it (once) and, |
||
if successful, will close it when done. |
|||
**mv** is shorthand for **move** |
|||
**mv** is shorthand for **move**. |
|||
Newer versions of foreach will use `_drag` (mimicking Stormfront drag-and-drop functionality) to reduce this from two |
|||
Newer versions of foreach will use `_drag` (mimicking Stormfront drag-and-drop) to reduce this from two commands to |
|||
commands to one when they think it is safe to do so. |
|||
one when they think it is safe to do so. |
|||
**fastmove** _[what]_ **to** _where_ |
|||
A somewhat faster version of the above, but more prone to failing if you have oddly scripted containers involved or |
|||
tasks that involve roundtime. It utilizes typeahead -- namely sending the GET and PUT portions at the same time. |
|||
This can be shortened as **fastmv**, **fmove** or **fmv**. |
|||
Like **move**, newer versions will use `_drag` to collapse this to a single command when it is safe to do so. |
|||
**fastmove** _[what]_ **to** _where_ |
|||
This is a somewhat faster version of the above, but it's more prone to failing if you have oddly scripted containers |
|||
involved or tasks that involve roundtime. It utilizes typeahead -- namely sending the GET and PUT portions at the |
|||
same time. |
|||
This can be shortened as **fastmv**, **fmove** or **fmv** |
|||
**stash** _[what]_ |
|||
Newer versions of foreach will use `_drag` (mimicking Stormfront drag-and-drop functionality) to reduce this from two |
|||
This tries to store _what_ in your defined lootsack. If it is full, it tries lootsack2, and so on. |
|||
You can set up your lootsacks with `;vars` -- e.g. |
|||
**stash** _[what]_ |
|||
This tries to store _what_ in your defined lootsack. If it is full, it tries lootsack2 and so on. |
|||
You can setup your lootsacks with `;vars` -- e.g. |
|||
``` |
``` |
||
;vars set lootsack=backpack |
;vars set lootsack=backpack |
||
;vars set lootsack2=cloak |
;vars set lootsack2=cloak |
||
``` |
``` |
||
Lootsacks that stop being in your inventory while **Foreach** is running will stop having items fed to them. |
Lootsacks that stop being in your inventory while **Foreach** is running will stop having items fed to them. If every |
||
lootsack is full, foreach pauses so you can make room (or kill it). |
|||
**giveitem** [**to**] _player_ |
**giveitem** [**to**] _player_ |
||
If this is the *first* command, foreach adds an implicit `get item` before it. |
If this is the *first* command, foreach adds an implicit `get item` before it. |
||
Offers the item to the specified _player_ and waits for them to accept it before continuing. |
Offers the item to the specified _player_ and waits for them to accept it before continuing. The player name is |
||
autocompleted against PCs in the room: foreach errors out immediately if no matching PC, or more than one, is found. |
|||
This is only for players; NPCs don't produce the appropriate 'has accepted your offer' messaging. That said, you |
This is only for players; NPCs don't produce the appropriate 'has accepted your offer' messaging. That said, you |
||
don't need to wait for NPCs to accept item offers |
don't need to wait for NPCs to accept item offers anyway -- so just use `give` instead of `giveitem` for them. |
||
**appraise** or **appraise item** |
**appraise** or **appraise item** |
||
If this is the *first* command, foreach adds an implicit `get item` before it. |
If this is the *first* command, foreach adds an implicit `get item` before it. |
||
If you're still holding the item at the end of running all of the commands, it will be returned to its parent |
If you're still holding the item at the end of running all of the commands, it will be returned to its parent |
||
container. |
container. `appraise` by itself is shorthand for `appraise item`. |
||
`appraise` by itself is shorthand for `appraise item`. |
|||
**trash** or **trash item** |
|||
Disposes of the current item using the game's TRASH verb. As the first command, an implicit `get item` is added. |
|||
`trash` by itself is shorthand for `trash item`. |
|||
**return** |
**return** |
||
Equivalent to `put item in container`. Returns whatever item Foreach is currently processing to |
Equivalent to `put item in container`. Returns whatever item Foreach is currently processing to wherever it |
||
originally found it. Just like `put item in container`, this only works if the item is still in your hands (which |
originally found it. Just like `put item in container`, this only works if the item is still in your hands (which you |
||
can take advantage of in your commands). For worn targets it WEARs the item; for ground targets it PLACEs it. |
|||
== Control == |
|||
**echo** _message_ |
**echo** _message_ |
||
Echoes the specified text to your client. Not sent to the game. |
|||
**pause** |
**pause** |
||
Pauses ;foreach. This is particularly useful if you're using ;foreach to examine a set of items and |
Pauses ;foreach. This is particularly useful if you're using ;foreach to examine a set of items and make decisions |
||
about them. For instance, you can write a script to test gems for orbs, pause after casting/singing, and then |
about them. For instance, you can write a script to test gems for orbs, pause after casting/singing, and then |
||
move the gem to the correct container based on the result before unpausing foreach to continue to the next |
manually move the gem to the correct container based on the result before unpausing foreach to continue to the next |
||
gem. |
|||
**sleep _time_** |
|||
**sleep** _time_ |
|||
Pauses ;foreach. This is particularly useful if you're using ;foreach to examine a set of items and making decisions |
|||
Pauses for _time_ seconds before continuing. Decimals are allowed, e.g. `sleep 0.5`. |
|||
about them. For instance, you can write a script to test gems for orbs, pause after casting/singing, and then manually |
|||
move the gem to the correct container based on the result before unpausing foreach to continue to the next gem. |
|||
**waitrt** or **waitrt?** |
|||
Waits for roundtime to finish. If you are not currently in RT, `waitrt` assumes you are about to be and waits for it |
|||
**waitrt** or **waitrt?** |
|||
to happen. `waitrt?` only waits if you are currently in RT, and might miss RT that you're about to be in (i.e. due to |
|||
a command you sent that the server hasn't processed yet). |
|||
it to happen. `waitrt?` only waits if you are currently in RT, and might miss RT that you're about to be in (i.e. |
|||
due to a command you sent that the server hasn't received yet.) |
|||
These function identically to, and are implemented using, the corresponding functions in Lich. |
|||
These function identical to, and are implemented using, the corresponding functions in Lich. |
|||
**waitcastrt** or **waitcastrt?** |
**waitcastrt** or **waitcastrt?** |
||
As above, but waits for cast roundtime instead. The same limitations and caveats apply. |
As above, but waits for cast roundtime instead. The same limitations and caveats apply. |
||
**waitfor** _text_ |
**waitfor** _text_ |
||
Waits to see the specified bit of text. Just like Lich's `waitfor`, except only one string of text can be matched. |
Waits to see the specified bit of text. Just like Lich's `waitfor`, except only one string of text can be matched. |
||
Matching occurs in text mode (e.g. not the raw XML stream) |
Matching occurs in text mode (e.g. not the raw XML stream). |
||
**waitre** _pattern_ |
**waitre** _pattern_ |
||
Waits for text matching the regular expression _pattern_. Note that matching currently applies in XML mode. |
Waits for text matching the regular expression _pattern_. Note that matching currently applies in XML mode. |
||
You can use the `;logxml` or `;showxml` scripts to get an idea of what raw XML is being sent to the client. |
You can use the `;logxml` or `;showxml` scripts to get an idea of what raw XML is being sent to the client. |
||
[Regex101](https://regex101.com/) is a good resource for testing out patterns against text from the game before |
[Regex101](https://regex101.com/) is a good resource for testing out patterns against text from the game before |
||
trying them for real. |
|||
**waitmana** _amount_ or **waitmp** _amount_ |
|||
**waitmana** _amount_ or **waitmp** _amount_ |
|||
Waits until you have at least this much mana. |
Waits until you have at least this much mana. |
||
**waithealth** _amount_ or **waithp** _amount_ |
**waithealth** _amount_ or **waithp** _amount_ |
||
Waits until you have at least this many health points. |
Waits until you have at least this many health points. |
||
**waitspirit** _amount_ or **waitsp** _amount_ |
**waitspirit** _amount_ or **waitsp** _amount_ |
||
Waits until you have at least this much spirit. |
Waits until you have at least this much spirit. |
||
**waitstamina** _amount_ or **waitst** _amount_ |
**waitstamina** _amount_ or **waitst** _amount_ |
||
Waits until you have at least this much stamina. |
Waits until you have at least this much stamina. |
||
== Notes and gotchas == |
|||
* `;foreach` is blind to closed containers (except lockers, which it will open automatically and close again afterwards |
|||
If you think `;foreach` is doing something funny, here is a rough overview of the entire logic process from start to finish: |
|||
if it opened them). Append `?` to a named container to skip it gracefully if it is closed or missing. |
|||
* If `;foreach` is trusted (`;trust foreach`), its initial inventory-scan output is silenced so it doesn't spam your |
|||
Depending on the level of depth you want, either just read the bold parts, just read the first sentences, or read the whole thing. |
|||
client. Only the scan commands are squelched; the actual commands you asked for are still shown. |
|||
* Multiple instances can run at once (for example via `;force`). In Stormfront each instance gets its own status entry |
|||
**Option Parsing**: All of the options are interpreted, validated, and converted into a usable format |
|||
in the shortcut bar where it can be paused, resumed, or stopped. |
|||
* *(Advanced / unsupported.)* A command can be prefixed with `!` to skip some of foreach's usual checks in favor of |
|||
**Implicit Commands**: The command list is fixed up |
|||
speed. This is faster but riskier, and behavior may change between versions. Use at your own risk. |
|||
=== Technical Explanation === |
|||
If you think `;foreach` is doing something funny, here is a rough overview of the entire logic process from start to |
|||
finish. |
|||
Depending on the level of depth you want, either just read the bold parts, just read the first sentences, or read the |
|||
whole thing. |
|||
**Option Parsing**: All of the options are interpreted, validated, and converted into a usable format. |
|||
**Implicit Commands**: The command list is fixed up. |
|||
* Shortened versions of some verbs that Foreach has special handling for are expanded to their full versions. This |
* Shortened versions of some verbs that Foreach has special handling for are expanded to their full versions. This |
||
includes shortened versions of `drop`, `place`, `sell`, `appraise`, `register`, `get` |
includes shortened versions of `drop`, `place`, `sell`, `appraise`, `register`, `get`, `take`, `read`, `look`, |
||
`analyze`, `inspect` and `locker`. |
|||
* If `stash` is in the command list, a list of lootsacks is built and the script exits if it can't find any. |
* If `stash` is in the command list, a list of lootsacks is built and the script exits if it can't find any. |
||
* Any `giveitem` commands resolve to an exact player name in the room (and the script fails if no match is found). |
* Any `giveitem` commands resolve to an exact player name in the room (and the script fails if no match is found). |
||
* Commands like `place`, `sell`, `stash`, `appraise`, `mark` and `unmark` get the word `item` |
* Commands like `place`, `sell`, `trash`, `stash`, `appraise`, `register`, `mark` and `unmark` get the word `item` |
||
they already have something after them. (`sell` becomes `sell item` |
added to the end unless they already have something after them. (`sell` becomes `sell item`; `sell emerald` is |
||
unchanged.) |
|||
* *For the first command only:* An implicit `_remget item` (for worn targets) or `get item` (everything else") is |
|||
* *For the first command only:* An implicit `_remget item` (for worn targets) or `get item` (everything else) is |
|||
added for commands that are known to only work with an item in hand, such as `giveitem`, `place`, `sell`, `stash`, |
|||
added for commands that are known to only work with an item in hand, such as `giveitem`, `place`, `sell`, `trash`, |
|||
`appraise`, `register`, `mark` and `unmark`. This only applies if the command string is `get item`, i.e. |
|||
`appraise`, `register`, `mark` and `unmark`. (`_remget` is a hidden Foreach command equivalent to "remove the item |
|||
`get emerald` skips this check. |
|||
if its parent container is `_worn`, otherwise get it.") |
|||
* *If there is only one command:* For commands in the above list that don't dispose of an item (so not `sell`, |
|||
get it." |
|||
`trash`, `place` or `giveitem`), an implicit `return` is added to the end of the list. |
|||
* *If there is only one command*: For commands in the above list that don't dispose of an item (so not `drop` or |
|||
`sell`), an implicit `return` is added to the end of the list |
|||
**Item Scanning**: Foreach builds a list of containers and the items in them. |
|||
At this point, `foreach` has an empty map of container IDs to names (with fake IDs `_worn`, `_ground` and `_desc` for |
|||
**Item Scanning**: Foreach builds a list of containers and items that are in them. |
|||
those targets), an empty map of container IDs to lists of contents, and an empty list of containers to scan. |
|||
Throughout this process, container names are mapped as they're discovered (mainly for the item-listing mode if you |
|||
two targets), an empty map of container IDs to lists of container contents, and an empty list of containers to scan. |
|||
Throughout this process, container names are mapped as they're discovered (mainly for the item listing mode if you |
|||
`;foreach in ...` with no commands), some container IDs are added to the "to scan" list, and each container along |
`;foreach in ...` with no commands), some container IDs are added to the "to scan" list, and each container along |
||
with its items |
with its items is added to the "to filter" list. If you're filtering by status (marked/registered, e.g. |
||
`;foreach unmarked`), a table is made of items and their |
`;foreach unmarked`), a table is made of items and their statuses. |
||
* `inv` aborts if you specified at least one command but no item |
* `inv` aborts if you specified at least one command but no item _what_, to avoid affecting everything you own |
||
(unless you |
(unless you said `;foreach ALL in inv`). Otherwise it reads all of your items and their containers, saving status |
||
data if needed. |
|||
containers (first level results). If you're filtering by status, their status data is saved as well. |
|||
* `last` simply restores the container/item data from the previous ;foreach invocation. It fails immediately if |
* `last` simply restores the container/item data from the previous ;foreach invocation. It fails immediately if |
||
you're filtering by status. |
|||
* `loot` adds each container on the ground to the "to scan" queue. It fails immediately if you're filtering by |
* `loot` adds each container on the ground to the "to scan" queue. It fails immediately if you're filtering by |
||
status, as that information cannot be determined for items on the ground. |
|||
* |
* `locker` (premium): determines your location and reads the appropriate `locker manifest`. If `;foreach` can't |
||
determine your current location, it fails if you're filtering by status; otherwise it falls back to a legacy scan. |
|||
* `floor`/`ground`/`room` places the contents of the room (GameObj.loot) into the `_ground` container. Like `loot`, |
|||
locker manifest can show marked/registered for contents of a locker), and otherwise adds the locker containers to |
|||
it fails immediately if you're filtering by status. |
|||
the "to scan" queue. |
|||
* ` |
* `desc` places room-description items into the `_desc` container. It also cannot be filtered by status. |
||
`loot`, it fails immediately if you're filtering by registration/mark status |
|||
* `worn` places `GameObj.inv` into the `_worn` container. |
* `worn` places `GameObj.inv` into the `_worn` container. |
||
* Anything else is treated |
* Anything else is treated as a single named container: foreach LOOKs in (or on/under/behind) it to get the game to |
||
send its contents, determines its ID, and gathers status data if needed. Collections (e.g. mannequins) expand into |
|||
(only possible for non-premium lockers), ;foreach exits immediately with an error. Otherwise, it LOOKs in the |
|||
one scan per member. |
|||
container to get the game to send over its inventory data and determine what its ID and searches for status data |
|||
(if needed, see the "To Scan" section) |
|||
Then, if there are any containers to scan: |
Then, if there are any containers to scan: |
||
* The `sorter` script is paused, if |
* The `sorter` script is paused, if running, to avoid spam. |
||
* Each container is looked in (to get or refresh its inventory) |
* Each container is looked in (to get or refresh its inventory). |
||
* For each container |
* For each container, its contents are added to the "to filter" list, and (if filtering by status and the status is |
||
unknown) the necessary INV FULL / INV HANDS FULL commands are issued once and the data cached for reuse. |
|||
* Its contents are added to the "to filter" list |
|||
* If you are filtering by status and status data is unknown for this container: (The INV commands here are only done once and data saved for subsequent containers) |
|||
If the container is in your right hand or left hand, an `INV HANDS FULL` to get its registration status |
|||
Otherwise, an `INV FULL` is done, followed by an `INV HANDS FULL` if the container was not found in your inventory. |
|||
If, after all of the above, ;foreach doesn't have status data for the container (meaning it's probably not in your inventory), |
|||
it assumes that it won't have status data for any of its contents either and exits. |
|||
* The `sorter` script is resumed, if it was paused. |
* The `sorter` script is resumed, if it was paused. |
||
**Item Sorting**: If the SORTED or REVERSED options are specified, they are applied to each container inventory. |
|||
**Item |
**Item Sorting**: If the SORTED, NOUNSORTED or REVERSED options are specified, they are applied to each container's |
||
inventory. |
|||
including applying any options (e.g. `UNIQUE`, `MARKED`, `FIRST 5`). If you're filtering by status and any item is |
|||
not found in the the table, `;foreach` complains loudly and exits. This particular failure should never occur, |
|||
**Item Filtering**: For each container and list of items in to_filter, items are filtered based on your criteria, |
|||
since of all the related checks should have happened earlier. |
|||
including applying options (e.g. `UNIQUE`, `MARKED`, `FIRST 5`). Containers with no items left after filtering are |
|||
dropped. If no items remain at all, ;foreach exits. |
|||
Any containers that have items left after the filtering phase are removed from the relevant tables. |
|||
If no remaining items exist, ;foreach exits. |
|||
**Snapshot**: At this point, ;foreach knows every single item and container it is going to work on. This information |
**Snapshot**: At this point, ;foreach knows every single item and container it is going to work on. This information |
||
is saved for later |
is saved for later use by `;foreach in last`, and then the execution phase begins. |
||
**Execution**: For each container: |
**Execution**: For each container: |
||
* If there are no commands, pretty-print a list of matching contents in that container. |
* If there are no commands, pretty-print a list of matching contents in that container. |
||
* Otherwise, for each item |
* Otherwise, for each item, for each command: |
||
* Replace |
* Replace `item`, `noun`, `name` and `container` with the item ID, item noun, item name and container ID. |
||
* If the command starts with `$lich_char` (usually `;`), run it. |
* If the command starts with `$lich_char` (usually `;`), run it as a Lich script. |
||
* If this is |
* If this is one of the convenience shortcuts (e.g. MOVE) or control commands (e.g. PAUSE), handle it. A couple of |
||
noteworthy details: |
|||
* MOVE/FASTMOVE will try to learn the ID of containers it is moving |
* MOVE/FASTMOVE will try to learn the ID of containers it is moving things to, since `_drag` only works with IDs. |
||
When the IDs are known it uses `_drag`, collapsing the get+put into one command. |
|||
* STASH also tries to use `_drag` when possible, which is almost always, since lootsack IDs are determined at |
|||
startup and the IDs of 'item' are always known. |
|||
* STASH also tries to use _drag when possible, which is almost all the case since lootsack IDs are determined at startup |
|||
* Some commands define a "run this before the next command" procedure (usually `waitrt?` and sometimes waiting for |
|||
and the IDs of 'item' are always known. |
|||
a prompt). Most commands clear this, but some control commands (`waitfor`, `waitre`, `pause`, `sleep`) leave it |
|||
* Some commands define a "run this before the next command" procedure (usually consisting of 'waitrt?' and |
|||
for the next command to resolve. This prevents issues like trying to `waitfor` a message that would scroll by |
|||
sometimes waiting for a prompt). Most commands (including anything sent natively to the game) will execute |
|||
while in roundtime, or the script waiting to pause and thus not being unpausable until you are out of roundtime. |
|||
and clear this, but some control commands (like `waitfor`, `waitre`, `pause` and `sleep`) ignore it and let |
|||
the next command in the list resolve it. This prevents various issues like trying to `waitfor` a message that |
|||
would scroll by while in roundtime, or the script waiting to pause (and thus preventing it from being unpaused |
|||
until you are out of roundtime) |
|||
| Line 402: | Line 478: | ||
{{Third-Party Software}} |
{{Third-Party Software}} |
||
[[Category:Third-Party Software]] |
|||
{{top}} |
{{top}} |
||
Latest revision as of 10:54, 24 June 2026
Lich:Script foreach is a third party script and is not maintained by Simutronics. Simutronics is not responsible for the accuracy of the information presented on this page, nor is it liable for issues stemming from the use of the application on players' personal devices.
What is it?
foreach is a script for sending a series of commands for each item that matches its criteria.
While not a full replacement for "proper" purpose-built Lich scripts, it is still frequently useful in its own regard. Anywhere you would otherwise repeat the same handful of commands once per item -- moving boxes, cutting gems, reading scrolls, selling for a bounty -- foreach can do it in a single line.
I don't want to read docs, can you give me some examples?
No rogues around? Move all your boxes to your locker: `;foreach box in inv; move to locker`
Move all your boxes back out of your locker: `;foreach box in locker; move to backpack`
Own a gemcutter and want to cut all the gems in a container? `;foreach gem in red sack; move to gemcutter; turn gemcutter; waitrt; move to blue sack`
Getting boxes picked? `;foreach box in inv; get item; give item to Rogue; waitfor Rogue offers you; accept item` *(This'll only work if you're fast enough to empty boxes while the rogue is picking)*
Checking for orb gems? Sorcerer: `;foreach gem in queue bag; get item; waitmana 4; prep 704; cast item; pause; put item in reject bag` Bard using `;loresing`: `;foreach gem in queue bag; get item; waitfor You remove; waitmana 20; ;loresing purpose noun; pause; waitrt?; put item in reject bag` While the script is paused, put the gem in the orb bag if it's an orb. `;unpause` will put it in the reject bag if you're still holding it.
Bulk reading scrolls? `;foreach scroll in inv; read item`
Appraise every gem in a container (foreach gets each one, appraises it, and puts it back): `;foreach gem in backpack; appraise`
Accidentally move a bunch of items to the wrong container using foreach? `;foreach in last; move to container` *(back where they started)* *or* `;foreach in last; get item; move to correct backpack`
Turning in gems or herbs for a bounty? `;foreach name=uncut diamond in inv; get item; sell item`
Don't want to sell ALL of your diamonds for said bounty? `;foreach first 7 name=uncut diamond in inv; get item; sell item`
Sell everything in a container that the local pawnshop will buy: `;foreach sellable=pawnshop in my cloak; get item; sell item`
Doing a long task and want to be able to resume it later (possibly on the same source container)? `;foreach unmarked gem in wherever; get item; do lots of things; mark item; return` as many times as needed, followed by `;foreach marked gem in wherever; get item; unmark item` (`wherever` must be a container in your inventory or your entire `inv`)
Need to ditch your sorcerer wands but want to keep the wizard ones? `;foreach q=/(bloodwood|twisted|yew|bone|glass|thanot) wand/ in inv; sell` (This uses regular-expression pattern matching -- note the slashes.)
Match a specific full name (article and adjectives included): `;foreach name=*quartz orb in inv; get item; put item in locker`
That's nice. But what can it REALLY do?
Usage
;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**;** _command1_**;** _command2_**;** ...] ;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**/** _command1_**/** _command2_**/** ...] ;foreach** _[options]_ _[what]_ **in/on/under/behind** _targets_ [**|** _command1_**|** _command2_**|** ...]
Typing `;foreach` or `;foreach help` by itself prints the built-in help, including the live list of item types and sellable categories that Lich currently knows about.
Options
_options_ is a set of optional filters on what matching items `foreach` will operate on. It can consist of zero or more of the following:
**registered** _or_ **unregistered** Only matches items that are registered or not registered. This option is only available if the items in question are in your inventory or your premium locker. (`locker manifest` is used to determine item status in the latter case, which is not available for standard lockers.)
**marked** _or_ **unmarked** Only matches items that are marked or not marked as unsellable. This has the same restrictions as the above. You can use this in conjunction with MARKing items as part of your ;foreach script to prevent operating on the same item more than once in situations where you might run ;foreach multiple times.
**unique** Only the first item with any given full name will be matched. This is global across all containers, so if you have the same item in two different containers only the first item (whichever `foreach` sees first) will be matched.
**first _N_** Stop after _N_ matching items have been encountered. (The word `first` is optional, so `;foreach 5 gem ...` works.)
**skip _N_** _or_ **after _N_** Skip the first _N_ matching items.
**sort** _or_ **sorted** Sort items by full name within each respective container. Articles "a", "an", "some" and "the" are ignored when sorting. Containers are still handled one at a time, and the order of the containers themselves is not affected.
**nounsort** _or_ **nsort** _(also_ **nsorted** _/_ **nounsorted**_)_ Sort items by noun first, then by full name, within each respective container. Useful when you want all of one kind of item grouped together regardless of adjectives. As with **sort**, articles are ignored and container order is unaffected. (**sort** and **nounsort** are mutually exclusive -- specify at most one.)
**reverse** _or_ **reversed** Reverse the order of items within each container. If used alone, this means the last item in each container is processed first and vice versa. If used with **sorted** (or **nounsort**), items are handled in descending order rather than ascending. Containers are still handled one at a time, and the order of the containers themselves is not affected.
Options are applied roughly in the order listed above. You can combine **first** with **skip**: `;foreach first 5 after 10` will match items 11 through 15.
What
_what_ allows you to filter the types of items `foreach` acts on. It can be one of the following:
**type=**_pattern_ or **t=**_pattern_ or simply _pattern_ You can specify any type of item that Lich knows about. Examples include 'gem', 'wand' and 'scroll' (which includes things like palimpsests and other 'alternate names' for scrolls). `;foreach` by itself will show all the known types. `;foreach in inv` with no commands will show the types of all items it finds.
You can use `type=none` (or `type=unknown`) to explicitly find items that have no defined type according to Lich.
**Note:** This is only as accurate as Lich's own type data. You may be able to get more accurate type data by downloading and running the `gameobjadd` script at startup, or by keeping `gameobj-data.xml` up to date.
**sellable=**_pattern_ or **s=**_pattern_ You can match items by which shops will buy them, based on Lich's sellable data. For example, `;foreach sellable=pawnshop in my cloak; get item; sell item` will sell everything the pawnshop accepts. Use `sellable=none` (or `sellable=unknown`) to match items with no known sellable category. `;foreach` by itself lists every sellable category Lich currently knows about.
**noun=**_pattern_ or **n=**_pattern_ You can specify items that have a specific noun. This must be an exact match (unless using wildcards).
**name=**_pattern_ or **m=**_pattern_ You can specify items that have a specific name. The name is the short display name without the long description -- for example `uncut diamond`, `enruned mithril chest`, or `old scroll`. Matching is anchored, so the whole name must match unless you use wildcards (`name=*diamond`).
**fullname=**_pattern_ or **f=**_pattern_ You can specify items that have a specific full name. This is like `name=pattern`, but includes things like long descriptions. See `;foreach in inv` to get a list of item full names.
**quick=**_pattern_ or **q=**_pattern_ Identical to `fullname`, except the ends of _pattern_ are automatically wildcarded. `;foreach q=red,blue ...` is equivalent to `;foreach f=*red*,*blue* ...` (When used with a regular expression, `quick` behaves exactly like `fullname`.)
_pattern_ can be:
`one or more words including spaces`: Must be an exact match (subject to the anchoring rules of the chosen attribute). `*use*asterisks*for*wildcards`: Asterisks function as wildcards. `name=*snowflake-cut*` will find anything with 'snowflake-cut' anywhere in the name, for instance. `this,that`: Match any one of the options separated by commas. `blue sapphire,emerald,*diamond,*emerald,uncut ruby`: You can mix the above. `/(blue|white) crystal/`: You can match against a regular expression. The slashes are required, and matching is always case-insensitive. (Inline regex flags after the closing slash are not supported.)
All attribute matching is case-insensitive.
You can also use **ALL**, **ANY** or **EVERYTHING** as the _what_ value to deliberately match every item. This is required as a safety confirmation when running commands against your entire `inv` (see Targets, below).
Targets
_targets_ tells ;foreach where you want to look for the items. You can specify one or more _target_, separated by commas.
Each target can be the name of an actual container in game ("cloak", "my backpack", etc.), or one of the following options:
**inv** or **inventory** Examines the contents of all containers in your inventory. Note that containers in your hands are not in your inventory. As a safety measure, `;foreach in inv` with commands but no _what_ filter will refuse to run unless you explicitly say `;foreach ALL in inv; ...` (so you don't accidentally act on everything you own).
**worn** Examines items in your inventory, *not* their contents. Want to register everything you are wearing? `;foreach in worn; remove item; register item; wear item`
**floor** or **ground** or **room** Examines items on the ground. *The items, not their contents.*
**loot** Examines the contents of containers that are on the ground -- boxes, disks, errantly left-behind backpacks, etc.
**desc** Examines items mentioned in the room description (things that are part of the room rather than loose loot). Like `floor`/`loot`, this cannot be filtered by marked/registered status.
**locker** Examines the contents of your locker. *If your locker is not open, ;foreach will open it and close it when it is done.* On premium lockers it uses your `locker manifest` for a fast scan and to determine marked/registered status; if that fails it falls back to a slower legacy scan.
**last** or **previous** Last is the set of items that matched whatever the previous run of foreach found. You can use this to quickly undo things, like accidentally moving all of your inventory to your locker rather than just your boxes.
When using **last**, items are reported in the container they started the previous run in -- not their current location. So `;foreach in last; move to container` will move all (reachable) items back to their original locations. (Filtering **last** by marked/registered status is not supported.)
**fastinv** or **qinv** Similar to **inv**, but this uses container contents as currently known by Lich rather than doing an INV FULL. This allows for faster startup, but with two caveats: - Item registration/marked status is unavailable. (If you're using one of the relevant _options_, this will be automatically converted to `inv`.) - Containers that Lich doesn't know the contents of will be skipped. Most notably, if you have not LOOKed in a container since you logged in, that container will be skipped. Note that `;foreach in inv` does NOT satisfy the "looked in a container" requirement, but `;foreach in worn; look in item` probably will.
If you specified a _what_ (above section), it will further restrict the list of what foreach is working on.
- Container name suffix:** You can append a question mark to a named container so that foreach skips it gracefully
instead of failing if it is closed or missing. For example, `;foreach box in inv,disk?; move to locker` will still work even if you have no disk.
- Collective containers:** If LOOKing at a target reports a group -- for example "You see three mannequins. Looking at
the mannequins, you see a dark grey mannequin, ..." -- foreach will operate on every container in that list. So `;foreach on mannequins; ...` handles each mannequin in turn.
- Position keyword:** `in`, `on`, `under` and `behind` control how foreach LOOKs at named containers. Most containers
use `in`; use `on` for surfaces (tables, racks), and `under`/`behind` where appropriate.
Commands
Commands are separated by semicolons (`;`), slashes (`/`) or pipes (`|`). You can only use one type of separator in a given invocation of Foreach -- it's determined by the first one Foreach sees. The examples here all use semicolons, but the other two formats are useful if one of your commands needs to include semicolons itself (for example, when invoking a Lich script, or the lines of `LORESING`).
If you don't give Foreach any commands to perform, it simply spits out a list of all the matching items and some relevant stats (including item type, which makes `;foreach in backpack` a quick way to discover what `type=` value to use). This is useful, but usually you want to do something with items.
Any command you can send to the game can be used with Foreach. You can also invoke other Lich scripts by including the leading `;` (for example `;foreach gem in backpack; get item; ;my_script item; put item in container`). There are also a few shortcuts, conveniences, and some very basic scripting support (like waiting for a piece of text).
For commands to be worthwhile, there needs to be a way to refer to the current item. For this, there's some variable replacement rules -- as noted in the next section.
Replacements
Certain words within a command will be replaced with some detail about the current item. Here's a list.
**item** The most important replacement. This is replaced with an exact reference to an item using its unique ID. It's good for commands, but not particularly useful if you want to talk about the item in SAY or ECHO.
**container** This is like `item`, but refers to the container the item was found in. It won't work properly if the item was not in a container (e.g. when using the `floor`, `worn` or `desc` targets).
If you're using the `last` target, `container` refers to the container the item was *originally* in. Thus, the following will dump your inventory into your locker and then put it all back (aside from container orders becoming flipped):
`;foreach in inv; move to locker` `;foreach in last; move to container`
**noun** This will be replaced with the item noun, e.g. `diamond`, `chest`, `scroll`.
**name** This will be replaced with the item name, e.g. `uncut diamond`, `enruned mithril chest`, `old scroll`.
Shortcuts
For some normal game commands, typing `command` by itself is equivalent to `command item`. Also, if one of these commands is the first command in the list, an implicit `get item` (or `remove item`, for worn targets) may be added automatically when the command needs the item in hand. These commands currently are: **drop**, **place**, **sell**,
- appraise**, **register**, **mark**, **unmark**, **get**, **take**, **read**, **look**, **inspect**, **analyze**, and
- trash**.
When the *only* command is one that does not dispose of the item -- `appraise`, `register`, `mark` or `unmark` -- an implicit `return` is also added at the end, so the item is put back where it came from. (Commands that get rid of the item, such as `drop`, `sell`, `trash`, `place` and `giveitem`, do not get an implicit return.)
In addition, the following convenience shortcuts exist:
**move** _[what]_ **to** _where_ Shorthand for getting _what_ and then putting it in _where_. If you omit _what_, 'item' is assumed. _where_ can be a container, or `ground`/`floor` to drop the item. If _where_ is your locker, `;foreach` tries to open it (once) and, if successful, will close it when done.
**mv** is shorthand for **move**.
Newer versions of foreach will use `_drag` (mimicking Stormfront drag-and-drop) to reduce this from two commands to one when they think it is safe to do so.
**fastmove** _[what]_ **to** _where_ A somewhat faster version of the above, but more prone to failing if you have oddly scripted containers involved or tasks that involve roundtime. It utilizes typeahead -- namely sending the GET and PUT portions at the same time.
This can be shortened as **fastmv**, **fmove** or **fmv**.
Like **move**, newer versions will use `_drag` to collapse this to a single command when it is safe to do so.
**stash** _[what]_ This tries to store _what_ in your defined lootsack. If it is full, it tries lootsack2, and so on.
You can set up your lootsacks with `;vars` -- e.g. ``` ;vars set lootsack=backpack ;vars set lootsack2=cloak ```
Lootsacks that stop being in your inventory while **Foreach** is running will stop having items fed to them. If every lootsack is full, foreach pauses so you can make room (or kill it).
**giveitem** [**to**] _player_ If this is the *first* command, foreach adds an implicit `get item` before it. Offers the item to the specified _player_ and waits for them to accept it before continuing. The player name is autocompleted against PCs in the room: foreach errors out immediately if no matching PC, or more than one, is found.
This is only for players; NPCs don't produce the appropriate 'has accepted your offer' messaging. That said, you don't need to wait for NPCs to accept item offers anyway -- so just use `give` instead of `giveitem` for them.
**appraise** or **appraise item** If this is the *first* command, foreach adds an implicit `get item` before it. If you're still holding the item at the end of running all of the commands, it will be returned to its parent container. `appraise` by itself is shorthand for `appraise item`.
**trash** or **trash item** Disposes of the current item using the game's TRASH verb. As the first command, an implicit `get item` is added. `trash` by itself is shorthand for `trash item`.
**return** Equivalent to `put item in container`. Returns whatever item Foreach is currently processing to wherever it originally found it. Just like `put item in container`, this only works if the item is still in your hands (which you can take advantage of in your commands). For worn targets it WEARs the item; for ground targets it PLACEs it.
Control
**echo** _message_ Echoes the specified text to your client. Not sent to the game.
**pause** Pauses ;foreach. This is particularly useful if you're using ;foreach to examine a set of items and make decisions about them. For instance, you can write a script to test gems for orbs, pause after casting/singing, and then manually move the gem to the correct container based on the result before unpausing foreach to continue to the next gem.
**sleep** _time_ Pauses for _time_ seconds before continuing. Decimals are allowed, e.g. `sleep 0.5`.
**waitrt** or **waitrt?** Waits for roundtime to finish. If you are not currently in RT, `waitrt` assumes you are about to be and waits for it to happen. `waitrt?` only waits if you are currently in RT, and might miss RT that you're about to be in (i.e. due to a command you sent that the server hasn't processed yet).
These function identically to, and are implemented using, the corresponding functions in Lich.
**waitcastrt** or **waitcastrt?** As above, but waits for cast roundtime instead. The same limitations and caveats apply.
**waitfor** _text_ Waits to see the specified bit of text. Just like Lich's `waitfor`, except only one string of text can be matched. Matching occurs in text mode (e.g. not the raw XML stream).
**waitre** _pattern_ Waits for text matching the regular expression _pattern_. Note that matching currently applies in XML mode. You can use the `;logxml` or `;showxml` scripts to get an idea of what raw XML is being sent to the client. [Regex101](https://regex101.com/) is a good resource for testing out patterns against text from the game before trying them for real.
**waitmana** _amount_ or **waitmp** _amount_ Waits until you have at least this much mana.
**waithealth** _amount_ or **waithp** _amount_ Waits until you have at least this many health points.
**waitspirit** _amount_ or **waitsp** _amount_ Waits until you have at least this much spirit.
**waitstamina** _amount_ or **waitst** _amount_ Waits until you have at least this much stamina.
Notes and gotchas
* `;foreach` is blind to closed containers (except lockers, which it will open automatically and close again afterwards if it opened them). Append `?` to a named container to skip it gracefully if it is closed or missing.
* If `;foreach` is trusted (`;trust foreach`), its initial inventory-scan output is silenced so it doesn't spam your client. Only the scan commands are squelched; the actual commands you asked for are still shown.
* Multiple instances can run at once (for example via `;force`). In Stormfront each instance gets its own status entry in the shortcut bar where it can be paused, resumed, or stopped.
* *(Advanced / unsupported.)* A command can be prefixed with `!` to skip some of foreach's usual checks in favor of speed. This is faster but riskier, and behavior may change between versions. Use at your own risk.
Technical Explanation
If you think `;foreach` is doing something funny, here is a rough overview of the entire logic process from start to finish.
Depending on the level of depth you want, either just read the bold parts, just read the first sentences, or read the whole thing.
**Option Parsing**: All of the options are interpreted, validated, and converted into a usable format.
**Implicit Commands**: The command list is fixed up.
* Shortened versions of some verbs that Foreach has special handling for are expanded to their full versions. This
includes shortened versions of `drop`, `place`, `sell`, `appraise`, `register`, `get`, `take`, `read`, `look`,
`analyze`, `inspect` and `locker`.
* If `stash` is in the command list, a list of lootsacks is built and the script exits if it can't find any.
* Any `giveitem` commands resolve to an exact player name in the room (and the script fails if no match is found).
* Commands like `place`, `sell`, `trash`, `stash`, `appraise`, `register`, `mark` and `unmark` get the word `item`
added to the end unless they already have something after them. (`sell` becomes `sell item`; `sell emerald` is
unchanged.)
* *For the first command only:* An implicit `_remget item` (for worn targets) or `get item` (everything else) is
added for commands that are known to only work with an item in hand, such as `giveitem`, `place`, `sell`, `trash`,
`appraise`, `register`, `mark` and `unmark`. (`_remget` is a hidden Foreach command equivalent to "remove the item
if its parent container is `_worn`, otherwise get it.")
* *If there is only one command:* For commands in the above list that don't dispose of an item (so not `sell`,
`trash`, `place` or `giveitem`), an implicit `return` is added to the end of the list.
**Item Scanning**: Foreach builds a list of containers and the items in them. At this point, `foreach` has an empty map of container IDs to names (with fake IDs `_worn`, `_ground` and `_desc` for those targets), an empty map of container IDs to lists of contents, and an empty list of containers to scan. Throughout this process, container names are mapped as they're discovered (mainly for the item-listing mode if you `;foreach in ...` with no commands), some container IDs are added to the "to scan" list, and each container along with its items is added to the "to filter" list. If you're filtering by status (marked/registered, e.g. `;foreach unmarked`), a table is made of items and their statuses.
* `inv` aborts if you specified at least one command but no item _what_, to avoid affecting everything you own
(unless you said `;foreach ALL in inv`). Otherwise it reads all of your items and their containers, saving status
data if needed.
* `last` simply restores the container/item data from the previous ;foreach invocation. It fails immediately if
you're filtering by status.
* `loot` adds each container on the ground to the "to scan" queue. It fails immediately if you're filtering by
status, as that information cannot be determined for items on the ground.
* `locker` (premium): determines your location and reads the appropriate `locker manifest`. If `;foreach` can't
determine your current location, it fails if you're filtering by status; otherwise it falls back to a legacy scan.
* `floor`/`ground`/`room` places the contents of the room (GameObj.loot) into the `_ground` container. Like `loot`,
it fails immediately if you're filtering by status.
* `desc` places room-description items into the `_desc` container. It also cannot be filtered by status.
* `worn` places `GameObj.inv` into the `_worn` container.
* Anything else is treated as a single named container: foreach LOOKs in (or on/under/behind) it to get the game to
send its contents, determines its ID, and gathers status data if needed. Collections (e.g. mannequins) expand into
one scan per member.
Then, if there are any containers to scan:
* The `sorter` script is paused, if running, to avoid spam.
* Each container is looked in (to get or refresh its inventory).
* For each container, its contents are added to the "to filter" list, and (if filtering by status and the status is
unknown) the necessary INV FULL / INV HANDS FULL commands are issued once and the data cached for reuse.
* The `sorter` script is resumed, if it was paused.
**Item Sorting**: If the SORTED, NOUNSORTED or REVERSED options are specified, they are applied to each container's inventory.
**Item Filtering**: For each container and list of items in to_filter, items are filtered based on your criteria, including applying options (e.g. `UNIQUE`, `MARKED`, `FIRST 5`). Containers with no items left after filtering are dropped. If no items remain at all, ;foreach exits.
**Snapshot**: At this point, ;foreach knows every single item and container it is going to work on. This information is saved for later use by `;foreach in last`, and then the execution phase begins.
**Execution**: For each container:
* If there are no commands, pretty-print a list of matching contents in that container.
* Otherwise, for each item, for each command:
* Replace `item`, `noun`, `name` and `container` with the item ID, item noun, item name and container ID.
* If the command starts with `$lich_char` (usually `;`), run it as a Lich script.
* If this is one of the convenience shortcuts (e.g. MOVE) or control commands (e.g. PAUSE), handle it. A couple of
noteworthy details:
* MOVE/FASTMOVE will try to learn the ID of containers it is moving things to, since `_drag` only works with IDs.
When the IDs are known it uses `_drag`, collapsing the get+put into one command.
* STASH also tries to use `_drag` when possible, which is almost always, since lootsack IDs are determined at
startup and the IDs of 'item' are always known.
* Some commands define a "run this before the next command" procedure (usually `waitrt?` and sometimes waiting for
a prompt). Most commands clear this, but some control commands (`waitfor`, `waitre`, `pause`, `sleep`) leave it
for the next command to resolve. This prevents issues like trying to `waitfor` a message that would scroll by
while in roundtime, or the script waiting to pause and thus not being unpausable until you are out of roundtime.
| Third-Party Software - edit |
|---|
| Lich Installation: Lich |
| Downloadable Lich Scripts: Go2 | Map | Repository | Popular Scripts |