-
Notifications
You must be signed in to change notification settings - Fork 59
teleport.lic Documentation
GemStone IV has special items that can teleport you between rooms — things like the enruned urglaes band, the archaic black ring, and other "n-setting teleporters." You wear or hold the item, twist it to a position number, rub it, and you jump to a saved location.
These items work fine on their own. The problem is they don't talk to ;go2 — your usual navigation script. So if you're trying to travel somewhere your teleporter could reach, go2 walks you the long way because it doesn't know the teleporter exists.
teleport.lic fixes that. It teaches your in-game map about every shortcut your teleporter offers, so when you run ;go2 <somewhere>, it'll automatically use the teleporter when that's faster.
You set it up once. After that, navigation just gets faster.
Two things:
-
A teleporter item. It needs to be in your inventory (worn or held). The script checks
GameObj.invfor it. - You need to know the item's exact name. Look at how the game describes it in your inventory — that exact text is what you'll tell the script.
Lich has a per-character variable system called ;vars. You'll set one called teleporter to your item's full name.
;vars set teleporter=enruned urglaes band
Replace enruned urglaes band with whatever your item is actually called. No quotes, no extra words — exactly what you'd type to refer to it.
To check what you set:
;vars get teleporter
To change it later (different item, or you got a new one):
;vars set teleporter=archaic black ring
;teleport
The first time you run it, the script will:
- Find the teleporter in your inventory.
- "Tap" it to read its menu of saved locations.
- Look up each location in your game map.
- Generate a Ruby file describing all the connections.
- Install those connections into your in-game map.
You'll see messages like building mapdb patch script for... and loaded patch script from.... That means it worked.
You don't want to remember to run ;teleport every time you log in. Add it to autostart:
;autostart add --global teleport
Now it'll run automatically every time you log a character in.
Once it's set up, you mostly don't think about it. Just run ;go2 <room_id> like normal. If your teleporter offers a faster route, go2 will use it.
If you want to teleport to a specific position on the menu without going through go2:
;teleport 1 # twists to position 1 and rubs
;teleport 5 # position 5
Position numbers come from your teleporter's menu — same numbers you'd use if you twisted it manually.
If you go into the n-setting menu and change one of the saved locations, the script's saved data is now out of date. Rebuild it:
;teleport --build
(--flush does the same thing — they're aliases.)
| Command | What it does |
|---|---|
;teleport |
Load the patch (build it if needed). |
;teleport <n> |
Manually teleport to position <n> (1–20). |
;teleport --build |
Rebuild from the live menu. Use this after changing n-settings. |
;teleport --flush |
Same as --build. |
;teleport --uninstall |
Remove all teleport shortcuts from your in-game map (until next reload). |
;teleport --debug |
Show extra info while building/loading. Useful if something looks wrong. |
The script saves files to a folder under your character's data directory:
DATA_DIR/teleporter/
├── enruned-urglaes-band.rb ← AUTOGENERATED — do not edit
├── archaic-black-ring.rb ← AUTOGENERATED — one per teleporter you've used
└── user.rb ← YOUR customizations — safe to edit, survives rebuilds
The autogenerated files have a banner at the top warning you not to edit them. If you do, your changes get wiped the next time you --build. Anything custom goes in user.rb.
You can find your DATA_DIR by running ;e echo DATA_DIR in-game.
user.rb is a Ruby file that runs every time the script loads. You don't need to be a Ruby expert to use it; you just need to recognize a few patterns and copy them.
If the file doesn't exist yet, create it at DATA_DIR/teleporter/user.rb. If it does, edit it.
There are three things you can do in user.rb:
Sometimes you stand in a room a lot — your bank, a shop, the inn — and want to be able to teleport out of it without manually walking back to a teleporter anchor first. Teleport.uni does this.
Teleport.uni(28813) # one room
Teleport.uni(410, 7138, 22308) # several at onceWhat this does: each room you list gets one-way connections to every teleporter anchor in the same zone. So if 28813 is in Wehnimer's Landing (a "west" zone) and your teleporter has anchors in Solhaven and Icemule, those connections get added.
It's one-way. It lets you teleport from these rooms; it doesn't make them destinations.
How to find a room's ID: stand in the room and run ;e echo Map.current.id.
Sometimes you want to add or override a specific connection — maybe between zones, or to a destination the auto-builder didn't catch:
Teleport.link(from: 25172, to: 28813, position: 1)This says: "from room 25172, you can reach room 28813 by teleporting to position 1." It's directional — if you want the reverse trip too, add a second line for that direction.
This is the most powerful part, and the one most worth understanding.
Every time go2 is figuring out a route, it asks: "if I use the teleporter here, how much does it cost?" You answer with a number (lower = better) or with nil (which means "don't use it for this trip").
By default, the answer is always 1 — meaning "always prefer the teleporter when possible." You override this in user.rb to add rules.
Here's what the override looks like:
Teleport::Callbacks.define_method :timeto do |from, to|
# rules go here
return 1 # default cost if no rule matched
endfrom and to are the room IDs of the trip being considered. You write if statements that return nil to disable, or a higher number to de-prioritize.
Here's a timeto callback with several rules. We'll go through them line by line.
Teleport::Callbacks.define_method :timeto do |from, to|
# Rule 1: don't teleport while doing escorts or child rescues
return nil if Script.running?("escort")
return nil if Script.running?("escortgo2")
return nil if Script.running?("echild")
return nil if Script.running?("move2")
# Rule 2: never use this specific room as part of any teleport trip
return nil if from == 25172 || to == 25172
# Rule 3: don't teleport while bigshot is actively hunting
# or while it's mapping out the hunting area's boundary
return nil if $bigshot_status.eql?(:hunting)
return nil if $bigshot_status.eql?(:boundary_building)
# Rule 4: when looting/herb scripts are running, allow teleport
# but make it less attractive (cost of 10 instead of 1)
return 10 if Script.running?("eloot")
return 10 if Script.running?("eherbs")
# Default: teleport is fine, normal cost
1
endEach rule is independent. The first one that matches wins, because return exits the block immediately. If nothing matches, the final 1 is the default.
A common situation: one of your teleporter anchors is in your hunting zone. You want go2 to use the teleporter to get to the hunting zone (because that's exactly the shortcut you saved), but you don't want it picked when you're not actually planning to hunt — and definitely not in the middle of a fight.
The cleanest rule: only auto-route through hunting-zone anchors when bigshot is running and not actively hunting. That captures "I'm at my hunting setup but currently resting/healing/looting between fights" — exactly when teleporting in or out makes sense.
Here's how that looks in user.rb:
HUNTING_ZONE = [12345].freeze # replace 12345 with your hunting-zone anchor IDs
Teleport::Callbacks.define_method :timeto do |from, to|
# ... your other rules go above ...
# Hunting zone: only auto-route when bigshot is running and not busy
if HUNTING_ZONE.include?(from) || HUNTING_ZONE.include?(to)
return nil unless Script.running?("bigshot")
return nil if $bigshot_status.eql?(:hunting)
return nil if $bigshot_status.eql?(:boundary_building)
end
1
endWhat each line does:
-
HUNTING_ZONE = [12345].freeze— list the room IDs you want gated. You can list more than one:[12345, 67890]. The.freezeis a Ruby convention for "this list never changes." -
if HUNTING_ZONE.include?(from) || HUNTING_ZONE.include?(to)— only apply the rule if the trip touches a gated room (either as the start or the destination). -
return nil unless Script.running?("bigshot")— if bigshot isn't running at all, block. Translation: "I'm not in hunting mode right now, don't route me near the hunting zone." -
return nil if $bigshot_status.eql?(:hunting)— if bigshot is running but actively hunting, block. Translation: "don't yank me out mid-fight." -
return nil if $bigshot_status.eql?(:boundary_building)— if bigshot is mapping out the hunting area's perimeter, block. It's doing inbounds checks that aren't:huntingstatus, but teleporting around mid-check would still cause it to misbehave. - Otherwise, the rule falls through and
1at the bottom applies — teleport allowed.
The result:
| What you're doing | Auto-routes through hunting anchor? |
|---|---|
| Bigshot not running | No |
| Bigshot running, resting | Yes ← this is the case you want |
| Bigshot running, hunting | No |
| Bigshot running, building boundary | No |
You manually run ;teleport <n>
|
Always works (callback doesn't apply) |
That last row is important: manual teleporting always works. The callback only controls automatic routing through go2. So even when the rules block auto-routing, you can still type ;teleport 3 to jump to position 3 directly.
user.rb runs for every teleporter you use. If you have rules that only apply to one item, branch on the description:
if Teleport.description =~ /archaic black ring/
Teleport.uni(330)
endTeleport.description is whatever you set with ;vars set teleporter=.... The =~ /regex/ syntax is Ruby's way of saying "matches this pattern."
Each character has their own data folder, so each one builds their own copy of the autogenerated files. To share customizations:
- Set up your teleporter's n-settings the same way on every character (so the autogenerated files match).
- Put your
user.rbin one place and copy or symlink it into each character'sDATA_DIR/teleporter/.
That way the auto-built shortcuts stay in sync, and your custom rules apply uniformly.
"could not find Room(<title>)" — the menu has a destination your map doesn't recognize. Update your map (;repo download map, then ;map --reload) and re-run ;teleport --build.
"non-unique anchor point detected" — two rooms in your map have the same title, and the script can't tell which one your teleporter is set to. It picks the first match. If that's wrong, rename one of the n-settings to something unique and --build again.
go2 ignores the teleporter when it should use it. Check three things:
- Is the patch loaded? Run
;teleport. You should see "loaded patch script." - Is the item in inventory? Is
;vars get teleporterexactly the item's name? - Is your
timetocallback returningnilfor the trip? Add;teleport --debugand watch for the link being installed.
go2 uses the teleporter when it shouldn't. Your timeto callback is missing a rule, or a rule isn't matching. Add a return nil for the case you want blocked.
The shortcuts disappeared after I reloaded the map. They shouldn't — the script monkey-patches Map.load and Map.reload to re-apply automatically. If they did, run ;teleport to re-apply, and consider filing an issue.
I want to start over with a clean map. Run ;teleport --uninstall to strip everything, then ;teleport --build to rebuild from scratch.
;vars set teleporter=<full item name>
;teleport
;autostart add --global teleport
;teleport # load (auto on login if autostart is set)
;teleport <n> # manual teleport to position n
;teleport --build # rebuild after changing n-settings
;teleport --uninstall # strip patches from current session
# Add jump-out shortcuts from a room
Teleport.uni(28813)
# Add a specific link
Teleport.link(from: 25172, to: 28813, position: 1)
# Override the cost / disable rules
Teleport::Callbacks.define_method :timeto do |from, to|
return nil if Script.running?("some_script")
return nil if from == 12345 || to == 12345
return 10 if Script.running?("eloot")
1
end
# Per-teleporter customization
if Teleport.description =~ /archaic black ring/
Teleport.uni(330)
end