diff --git a/.gitignore b/.gitignore index 397b4a7..ace48a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,13 @@ -*.log +logs/active/*.log +logs/active/*.tar.gz +logs/features/timer_data.tin +logs/features/wamgmap.log +logs/features/wamgsaver.log +notes/ +src/quow.db.old +src/wamgsaver.tin +src/constants.tin +src/timer.tin +src/testing.tin +src/gems.tin +bin/watcher diff --git a/Discworld b/Discworld index 2ea237e..508acfa 100755 --- a/Discworld +++ b/Discworld @@ -1,4 +1,10 @@ -cd ~/documents/MUD/Discworld +# Change to the 'discworld-tintin' directory and remove the input history file +cd "$( dirname -- "$( readlink -f -- "$0"; )"; )" +rm logs/history.log + +# Set hotkeys to switch in/out of "mobile" mode +tmux bind C-f resize-pane -x 80% +tmux bind C-g resize-pane -x 100% # No existing discworld session? Then create one if [[ ! $(tmux ls -F '#S') =~ discworld ]]; then @@ -6,53 +12,15 @@ if [[ ! $(tmux ls -F '#S') =~ discworld ]]; then DISC_SESSION_CREATE=1 # Zero out log files to keep size down - # rm logs/minimap.log && touch logs/minimap.log - # rm logs/mapdoortext.log && touch logs/mapdoortext.log - # rm logs/chat.log && touch logs/chat.log + rm logs/chat.log && touch logs/chat.log - # Start a new tmux session named "discworld" - tmux new-session -d -A -D -s discworld -fi - -# If we're in TMUX, or we just created a new session, make a layout -if [ -n "$TMUX" ] || [ "$DISC_SESSION_CREATE" = "1" ]; then - - # Kill any existing panes but the one we're "starting" in if in tmux already - if [ -n "$TMUX" ]; then - tmux kill-pane -a -t discworld:0.0 - fi - - # Create the chat monitor window on top - tmux split-window -v -b -l 4 - tmux send-keys 'clear && tail -fs .5 logs/chat.log' 'C-m' - - # Did we ask for the full layout? - if [[ $1 == "full" ]]; then - # Horizontal split and load spottimers - tmux select-pane -t discworld:0.0 - tmux split-window -h -l 28 - tmux send-keys './bin/dt' 'C-m' - - # Splice a section for group shield monitor - tmux split-window -v -p 50 - tmux send-keys './bin/grp' 'C-m' - - # Place the ASCII map at the bottom - tmux split-window -v -p 50 - tmux send-keys './bin/map' 'C-m' - fi - - # Cut back to main window, vertical split for mapdoortext - tmux select-pane -t discworld:0.0 - tmux split-window -v -b -l 5 - tmux send-keys './bin/mdt' 'C-m' - - # Select main pane again and load TinTin++ with our config - tmux select-pane -t discworld:0.0 - tmux send-keys 'clear && tt++ -G config.tin' 'C-m' + # Start a new tmux session named "discworld" with our tmuxp setup + tmuxp load -s discworld ./discworld2.yaml fi +# If there is already a session, just attach if [ -z "$TMUX" ]; then + #tmux send-keys 'clear && tt++ -G config.tin' 'C-m' echo "Not already in TMUX, so let's attach" # If we aren't already inside of TMUX, then attach to our session tmux -2 attach-session -t discworld -d diff --git a/README.md b/README.md index 62f56aa..7f38504 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,221 @@ # DiscworldMUD-TinTin +A collection of configurations and scripts for use with [DiscworldMUD](http://discworld.starturtle.net) in the [TinTin++](https://tintin.mudhalla.net/) MUD client. Created by [Ruhsbaar](http://discworld.starturtle.net/lpc/secure/finger.c?player=ruhsbaar) of the Venerable Council of Seers. -## Overview -A collection of configurations and scripts for use with [DiscworldMUD](http://discworld.starturtle.net) in the [TinTin++](https://tintin.sourceforge.io) MUD client. Many features are meant to be run in their own "miniwindow" for which I make liberal use of Tmux panes. +Majorly updated in 2026 to work with latest TinTin++ versions (2.02.60 and beyond) and account for latest MUD features. -For reference, I host this configuration on a Linux VPS and thus am able to "take over" the Tmux session as I move between devices/lose internet without being disconnected from the game. This may help to explain some decisions made. +High-level feature list (not exhaustive): +* Chat window to capture tells/group say/talkers +* Speedwalk generation and room/npc/item searching via Quow's database +* Map door text parsing and ranking similar to Quow's +* Hotspot timers for hunting (sync to other users, report to group) +* Mindspace and spellcheck calculations (similar to Quow's) +* Group shields monitor +* Mission timers (sailing, sausage, am job market, dead letters) +* Hundreds of triggers and color highlights for sailing, combat, magic, and others +* Plenty more... -![Screenshot](https://i.imgur.com/dZkaqR6.png) +## Table of Contents +* [Introduction / Overview](#introduction--overview) +* [Installation and Setup](#installation-and-setup) + * [Installing the Package](#installing-the-package) + * [1. Install TinTin++ 2.02.60 or Later](#1-install-tintin-20260-or-later) + * [2. Installing Discworld-TinTin Package](#2-installing-discworld-tintin-package) + * [Configuring the MUD](#configuring-the-mud) + * [Columns Configuration](#columns-configuration) + * [ASCII Map Location](#ascii-map-location) +* [Features](#features) + * [Speedwalking and Room/NPC/Item Search](#speedwalking-and-roomnpcitem-search) + * [Room Search](#room-search) + * [NPC Search](#npc-search) + * [NPC Inventory Search](#npc-inventory-search) + * [Vendor Item Search](#vendor-item-search) + * [Gatherable Item Search](#gatherable-item-search) + * [Filtering Search by Region](#filtering-search-by-region) + * [Bookmark System](#bookmark-system) + * [Updating Database](#updating-database) + * [Map Door Text Parsing](#map-door-text-parsing) + * [MDT Configuration](#mdt-configuration) + * [Temporary Targets](#temporary-targets) + * [MDT Window and Implementation Reference](#mdt-window-and-implementation-reference) + * [Mission Cooldown Timers](#mission-cooldown-timers) + * [Missions Cooldown Report](#missions-cooldown-report) + * [Timed Alerts](#timed-alerts) + * [Save Across Restarts](#save-across-restarts) + * [Chat and Talker Logging](#chat-and-talker-logging) + * [Adding New Channels / Clubs](#adding-new-channels--clubs) + * [Talker Channel Logging](#talker-channel-logging) + * [Log File and Window Display](#log-file-and-window-display) + * [Session Logging](#session-logging) + * [Mindspace Calculator](#mindspace-calculator) + * [Spells Output and Gained/Lost](#spells-output-and-gainedlost) + * [Mindspace Skills Ingestion](#mindspace-skills-ingestion) + * [Spellcheck and Spell Lookup](#spellcheck-and-spell-lookup) + * [Spellbook Detail](#spellbook-detail) + * [Orders Primaries Tracking](#orders-primaries-tracking) + * [Sailing Mission Helpers](#sailing-mission-helpers) + * [Cooldown Tracking](#cooldown-tracking) + * [Per-Mission XP and Duration Report](#per-mission-xp-and-duration-report) + * [Stage Tracking](#stage-tracking) + * [Sail Colour Highlights](#sail-colour-highlights) + * [Group Features](#group-features) + * [Group Shield Monitor](#group-shield-monitor) + * [Commands](#commands) + * [Window / Display File](#window--display-file) + * [TPA and EFF Alerts](#tpa-and-eff-alerts) + * [Group EFF Drop Tracker](#group-eff-drop-tracker) + * [Group Weapon Snatch Tracker](#group-weapon-snatch-tracker) + * [XP Tracker](#xp-tracker) + * [Windows](#windows) + * [Commands](#commands-1) + * [Hotspot Timers](#hotspot-timers) + * [Window / File Display](#window--file-display) + * [Reset Spots](#reset-spots) + * [Reset A Spot](#reset-a-spot) + * [Reset All Spots](#reset-all-spots) + * [Sync With Others](#sync-with-others) + * [Share With Group](#share-with-group) + * [Colouring](#colouring) + * [Save Across Restarts](#save-across-restarts) + * [TinTin Prompt Bar](#tintin-prompt-bar) + * [TPA Monitor](#tpa-monitor) + * [Experience Gain Display](#experience-gain-display) + * [Thief Quota Timer](#thief-quota-timer) + * [Current Local Time](#current-local-time) + * [Thief Quota Helper](#thief-quota-helper) + * [Left to Steal Calculation](#left-to-steal-calculation) + * [Quota Flip Alerts](#quota-flip-alerts) + * [Prompt Bar Timer](#prompt-bar-timer) + * [Other Color Highlights and Triggers](#other-color-highlights-and-triggers) +* [Tmux Setup](#tmux-setup) + * [Toggle Full or Mobile View](#toggle-full-or-mobile-view) +* [Mouse Support](#mouse-support) + * [Scrolling Buffer](#scrolling-buffer) + * [Input Bar](#input-bar) + * [Clickable Links](#clickable-links) +* [Command Reference](#command-reference) + * [XP Tracker Commands](#xp-tracker-commands) + * [Hotspot Timer Commands](#hotspot-timer-commands) + * [Mission Commands](#mission-commands) + * [Group Commands](#group-commands) + * [Database Search and Speedwalk Commands](#database-search-and-speedwalk-commands) + * [Talker and Chat Commands](#talker-and-chat-commands) + * [Mindspace Commands](#mindspace-commands) + * [Quota Alert Commands](#quota-alert-commands) + * [Map Door Text Commands](#map-door-text-commands) + * [Utility Commands](#utility-commands) + * [Sizing Commands](#sizing-commands) +* [Credits](#credits) + * [Quow](#quow) + * [Oki](#oki) +* [Disclaimer](#disclaimer) -## Features and Bits +--- +## Introduction / Overview +This tintin++ setup has been crafted by me with love over the last ten years and has a wide array of features and functionality relating to many activities on DiscworldMUD. Many of the best features from Quow's excellent MUSHClient setup have been "converted" to work in TinTin++ so I could go back to my favourite client and be at home in my terminal window. -### Windows +Many features create output-files meant to be displayed in their own "mini window" for which I make liberal use of Tmux panes to display information alongside the standard TinTin++ window. -* Chat Monitor -* Map Door Text parser -* Hotspot timers -* Group shield monitor -* ASCII map +For reference I host this configuration on a Linux VPS and thus am able to "take over" the Tmux session as I move between devices (desktop, phone, laptop, work) without having to disconnect/reconnect to the MUD itself. This may help to explain some of the decisions made with the setup. That said it will work perfectly fine on your local computer. -### Commands and Scripts +*TODO* Update screenshot +![Screenshot](https://i.imgur.com/dZkaqRg.png) -#### Dead Letter Solver -* Command: `sl` -* File: `src/deadletter.tin` +## Installation and Setup +A short guide to getting the client installed, this package put in place and MUD options properly configured. -Read a letter in your inventory and print a solution, with the term to scry highlighted. Mostly uses the data from the Dead Letters page on the [wiki](https://dwwiki.mooo.com/wiki/Dead_letter_office_jobs). Works only for **difficult customers** and **distant lands**. +### Installing the Package -#### Prompt: TPA/XP/Quota Monitor -* Files: `src/prompt.tin`, `src/xpmonitor.tin`, `src/tpamonitor.tin`, `src/quota.tin` +#### 1. Install TinTin++ 2.02.60 or Later +* Your distribution may have TT++ in its package manager, but ensure you are getting at least v2.01.92, ideally v2.02.60 or later. +* I recommend building TinTin++ from [source](https://github.com/scandum/tintin) +* Requirements for build: + - pcre2 (libpcre2-dev or similar) + - zlib (zlib1g-dev or similar) + - gnutls (libgnutls30-dev or similar) +* See the offical TinTin++ installation [instructions](https://github.com/scandum/tintin/blob/master/INSTALL) for a complete guide +* Once you can type "tt++" into your shell and launch the TinTin++ client this step is complete -A tintin++ split across the top of the MUD window proper that displays a few bits of information: -1. How long has the session been running -2. How much XP you've earned total this session -3. The average XP/hour rate over that time -4. Do you have a TPA active -5. What colour is your TPA currently -6. How long until quota flips (appears if relevant) -7. Quota timer is coloured when in the last hour -8. In-game alerts for 1 hour, 30m, 10m left of quota +#### 2. Installing Discworld-TinTin Package +* Download this repository (git clone or download a tarball) +* Requirements for use: + - tmux and [tmuxp](https://github.com/tmux-python/tmuxp) (sudo apt install tmux tmuxp) + - python 3 + - sqlite 3 +* With package downloaded and requirements installed then `tt++ -G config.tin` will launch +* "Discworld" shell-file exists to setup tmux layout or connect to existing + - Recommend symlinking e.g. (`sudo ln -s /home/myuser/discworld-tintin/Discworld /usr/local/bin/disc) + - Now you can type "disc" from anywhere to launch or connect to the setup -#### Map Door Text GMCP Parser -* Files: `src/mdtparse.py`, `mdtconfig.json`, `src/gmcp.tin` -* Blatantly copied from [Quow's](http://quow.co.uk) wonderful work for his MUSHclient configs. +### Configuring the MUD -This python script (`src/mdtparse.py`) parses the mapdoortext data delivered by GMCP as you move rooms and displays a nice formatted list. Customizable with a configuration file (`mdtconfig.json`) that allows for picking out particular NPCs, assigning them point values, and colouring. +#### Columns Configuration +In order for lengthier triggers to catch you need to play with `cols 999` configured on the MUD to avoid line-breaks. This has the consequence of making many of the "formatted output" commands on the MUD look terrible (e.g. `who` or `skills`). -#### Item/NPC/Room Search -* Files: `src/db.tin` -* Made possible by [Quow's](http://quow.co.uk) fantastic data collection and publicly available database. +Because of this there are a number of aliases defined (in `src/aliases.tin`) that send `cols 60`, then the command, then `cols 999` the the MUD so you get pretty output but then revert back to full-length lines. -A series of TinTin aliases that invoke SQLite to search through Quow's database and retrieve various information about game items, people and places. +Commands that resize output before running: +* `skills` +* `speak` and `language` and `languages` +* `spells` +* `rituals` +* `group status` +* `who` +* `help` +* `nickname` -##### Item Search -> `db item sword`, `db npcitem sword` +#### ASCII Map Location +You must force the MUD to show the ASCII map above look/glance output, use the following commands to configure this properly: + +* `options output map glance = top` +* `options output map glancecity = top` +* `options output map look = top` +* `options output map lookcity = top` + +# Features + +## Speedwalking and Room/NPC/Item Search +* Files: `src/db.tin` (`src/maproute.py`, `src/quow.db`) + +A suite of commands for interacting with [Quow's](http://quow.co.uk) database of DiscworldMUD rooms, npcs, items and more. This allows for generating a *SPEEDWALK* route from your current location to a given result, if possible. Many of the output options from these commands are *CLICKABLE* with left-mouse if you prefer that to typing out the appropriate followup command. + +As a quick example typing `db room Mended Drum {am}` will search for rooms whose short names contain Mended Drum and are in Ankh-Morpork (see [filtering](#filtering-search-by-region)) and display up to 10 results. To speedwalk to one of the results you would click on it or type `db route [0-9]` where `[0-9]` is the numeric index of the returned results you want to target (see screenshots and deeper explanation below). Finally type (or click on) `speedwalk` to begin moving towards that location. Note that **all searches are case-sensitive* meaning "mended drum" is not the same as "Mended Drum". + +There's also a [*bookmarks*](#bookmark-system) feature to store frequently visited rooms for quick route generation. + +Get help with using these commands directly within TinTin++ by typing `db help`. + +### Room Search +Search for a room in the MUD by name. The name of a room is what you see when you `glance` (or `look` with `shortinlong=on` option enabled), for example `The north end of Short Street outside the Mended Drum` or `The Job Market centre`. Lets imagine we want to speedwalk from our current location to the "launder room" inside of the Ankh-Morpork theives guild to repair equipment. + +First, we'd search for the room by name: +> `db room launder` + +This would return all matching rooms (of which there are two). Note you could search for rooms specifically in Ankh-Morpork (or elsewhere) by using a [filter](#filtering-search-by-region). The results of `db room launder` will look like this: ``` -Results for items matching "sword": - [0]: sword necklace (A$4.25) found in occult jewellery stall, Ankh-Morpork - [1]: short sword (2,66Gl) found in dwarfish blacksmith's, Genua - [2]: long sword (4,0Gl) found in dwarfish blacksmith's, Genua - [3]: bastard sword (20Rh 100s) found in illegal contraband stall, Bes Pelargic - [4]: double-edged broad sword (A$112.50) found in weapon shop, Ankh-Morpork - [5]: watered-steel long sword (A$180) found in weapon shop, Ankh-Morpork - [6]: rose-hilted long sword (A$180) found in weapon shop, Ankh-Morpork - [7]: heavy sword [build-a-bear] (8Rh 60s) found in Assemble an Animal Workshop, Bes Pelargic - [8]: short sword [build-a-bear] (8Rh 60s) found in Assemble an Animal Workshop, Bes Pelargic - [9]: short sword scabbard (A$1) found in Sartorn and Son Scabbard Store, Ankh-Morpork +Results for rooms matching "launder". + [0]: launder room (inside) found in AM Thieves + [1]: launder room (inside) found in Genua Sewers +Generate a walking path with "db route #" or *clicking* the number or name of one of the results above. ``` -The "item" keyword is searching shops, while "npcitem" searches NPC inventories. +From here if we want to speedwalk to one of the results simply left-mouse click on one of the numbers (0 for AM, 1 for Genua) or room names ("launder room (inside)") to generate a route, or type `db route 0` to generate a route to the first result or any other. If a route is able to be generated a message will display showing the total # of steps and will set the speedwalk alias. The message will look like: +``` +[speedwalk] Generating speedwalk from current location to launder room, AM Thieves... +[speedwalk] A route was found, type "speedwalk" to fast travel. [140 steps] +``` +Now simply type `speedwalk` or left mouse-click on `"speedwalk"` to begin moving towards the AM launder room. -##### NPC Search +### NPC Search +Search for an NPC in the MUD by name. Let's imagine we want to locate a "villager" NPC and speedwalk to it from our current location. + +First, we'd search for NPCs matching "villager": > `db npc villager` + +This would return up to 10 matching NPCs. Note you could search for NPCs in specific areas by using a [filter](#filtering-search-by-region). The results of `db npc villager` will look like this: ``` Results for NPCs matching "villager". [0]: villager [Hillshire] found in road running through Hillshire, Sto Plains Region @@ -83,232 +225,644 @@ Results for NPCs matching "villager". [4]: villager found in cobbled road in Blackglass, Uberwald Region [5]: villager [ramtop villages] found in road outside of Brass Neck, Ramtops Regions [6]: villager [Blackglass] found in cobbled road in Blackglass, Uberwald Region +Generate a walking path with "db route #" or *clicking* the number or name of one of the results above. ``` -##### Room Search -> `db room launder` +From here if we want to speedwalk to one of the results simply left-mouse click on one of the numbers (0 for Hillshire, 5 for Brass Neck) or NPC names ("Sheepridge villager") to generate a route, or type `db route 0` to generate a route to the first result or any other. If a route is able to be generated a message will display showing the total # of steps and will set the speedwalk alias. The message will look like: ``` -Results for rooms matching "launder". - [0]: launder room (inside) found in AM Thieves - [1]: launder room (inside) found in Genua Sewers +[speedwalk] Generating speedwalk from current location to dark alley, Sto Plains Region... +[speedwalk] A route was found, type "speedwalk" to fast travel. [123 steps] +``` +Now simply type `speedwalk` or left mouse-click on `"speedwalk"` to begin moving towards the desired villager. + +### NPC Inventory Search +Search for an item in NPC inventories by name. Let's imagine we want to locate a "white Djelian loincloth" for a Mr Werks delivery mission, and we want to find it on an NPC so we don't have to pay for it... + +First, we'd search for the item in NPC inventories by name: +> `db npcitem white Djelian loincloth` + +This would return up to 10 matching NPCs who are known to have this item. Note you could search in specific areas using a [filter](#filtering-search-by-region). The results of `db npcitem white Djelian loincloth` will look like this: +``` +Results for NPC items matching "white Djelian loincl": + [0]: white Djelian loincloth found on palace guard in Square of Ancestors, Djelibeybi +Generate a walking path with "db route #" or *clicking* the number or name of one of the results above. ``` -##### Gatherable Spice Search +From here if we want to speedwalk to one of the results simply left mouse-click on one of the numbers or names in the result output to generate a route, or type `db route #` to generate a route to a specific result (0-9). If a route is able to be generated a message will display showing the total # of steps and will set the speedwalk alias. The message will look like: +``` +[speedwalk] Generating speedwalk from current location to Square of Ancestors, Djelibeybi... +[speedwalk] A route was found, type "speedwalk" to fast travel. [8 steps] +``` +Now simply type `speedwalk` or left mouse-click on `"speedwalk"` to begin moving towards the desired villager. + +### Vendor Item Search +Search for an item sold in shops or by vendor NPCs by name. Let's imagine we need a sword to swing, so we want to find a shop that sells swords. + +First, we'd search for the item by name: +> `db item sword` + +This would return up to 10 matching items found in shop or vendor lists. Note you could search in specific areas (AM, BP, DJB) using a [filter](#filtering-search-by-region). The results of `db item sword` will look like this: +``` +Results for items matching "sword": + [0]: bastard sword (20Rh 100s) found in illegal contraband stall, Bes Pelargic + [1]: double-edged broad sword (A$112.50) found in weapon shop, Ankh-Morpork + [2]: watered-steel long sword (A$180) found in weapon shop, Ankh-Morpork + [3]: rose-hilted long sword (A$180) found in weapon shop, Ankh-Morpork + [4]: heavy sword [build-a-bear] (8Rh 60s) found in Assemble an Animal Workshop, Bes Pelargic + [5]: short sword [build-a-bear] (8Rh 60s) found in Assemble an Animal Workshop, Bes Pelargic + [6]: swordfish (DjToon 7.75) found in smelly fish stall, Djelibeybi + [7]: sword necklace (A$4.25) found in Rainbow Homeopaths Incorporated, Ankh-Morpork + [8]: long sword (A$3) found in Follatt Biraten's pawn shop, Ankh-Morpork + [9]: swordfish (S8|84de) found in seafood-scented edge of the Harbour Market, Ephebe +Generate a walking path with "db route #" or *clicking* the number or name of one of the results above. +``` + +From here if we want to speedwalk to one of the results simply left mouse-click on one of the numbers or item names in the result output to generate a route, or type `db route #` to generate a route to a specific result (0-9). If a route is able to be generated a message will display showing the total # of steps and will set the speedwalk alias. The message will look like: +``` +[speedwalk] Generating speedwalk from current location to weapon shop, Ankh-Morpork... +[speedwalk] A route was found, type "speedwalk" to fast travel. [136 steps] +``` +Now simply type `speedwalk` or left mouse-click on `"speedwalk"` to begin moving towards the shop with the sword you want. + +### Gatherable Item Search +Search for gatherable items like spices by name. Let's imagine we need to find some Thyme to complete a spiced sausage mission. + +First, we'd search for a gatherable item by name: > `db gatherable thyme` + +This would return up to 10 matching locations where thyme can be gathered. Note you could search in specific areas using a [filter](#filtering-search-by-region). The results of `db gatherable thyme` will look like this: ``` -Results for gatherables matching "thyme". +Results for gatherables matching "thyme": [0]: some thyme found in garden, Bes Pelargic - [1]: some thyme found in bottom part of the Temple gardens, Temple of Small Gods - [2]: some thyme found in conservatory, BP Estates - [3]: some thyme found in neat herb garden, Ramtops Regions - [4]: some thyme found in service entrance to the Chronides farmstead, Klatchian Farmsteads - [5]: some thyme found in northeast garden, Sto Plains Region - [6]: some thyme found in garden, Ramtops Regions - [7]: some thyme found in cottage herb garden, Ramtops Regions + [1]: some thyme found in kitchen garden of the Beard and Badger, + [2]: some thyme found in bottom part of the Temple gardens, Temple of Small Gods + [3]: some thyme found in conservatory, BP Estates + [4]: some thyme found in neat herb garden, Ramtops Regions + [5]: some thyme found in service entrance to the Chronides farmstead, Klatchian Farmsteads + [6]: some thyme found in northeast garden, Sto Plains Region + [7]: some thyme found in garden, Ramtops Regions + [8]: some thyme found in cottage herb garden, Ramtops Regions + [9]: some thyme found in cosy kitchen hut, +Generate a walking path with "db route #" or *clicking* the number or name of one of the results above. ``` -##### Search Filter By Area ->`db room temple {djb}`, `db npc guard {am}`, `db npcitem sword {ramtops}` - -All of the above searches (room, npc, npcitem, item) are filterable by area, just include the area name in curly braces after your search terms. See below for a breakdown of possible areas. - +From here if we want to speedwalk to one of these places simply left mouse-click on one of the names or numbers in the output or type `db route #` to generate a route to a specific result (0-9). If a route is able to be generated a message will display showing the total # of steps and will set the speedwalk alias. The message will look like: ``` - am/ankh-morpork - bp/bes pelargic - djb/djelibeybi - ephebe - genua - ramtops - sto - sto plains - sto-lat +[speedwalk] Generating speedwalk from current location to service entrance to the Chronides farmstead, Klatchian Farmsteads... +[speedwalk] A route was found, type "speedwalk" to fast travel. [24 steps] +``` +Now simply type `speedwalk` or left mouse-click on `"speedwalk"` to begin moving towards the room where you can gather. + +### Filtering Search by Region +All of the database (`db`) search commands described above can have their results limited to a specific region or city of the Discworld by including a filtering clause at the end of the command wrapped in curly-braces. + +Some examples of this: +* `db room Mended {am}` would search for rooms with "Mended" in the name only in Ankh-Morpork +* `db item sword {ephebe}` would search for swords for sale only in Ephebe +* `db gatherable thyme {bes pelargic}` would search for thyme in Bes Pelargic + +The valid filter options are as follows: +* `am` or `ankh-morpork` +* `djb` or `djelibeybi` +* `genua` +* `sto` or `sto plains` +* `bp` or `bes pelargic` +* `ephebe` +* `ramtops` +* `sto-lat` + +### Bookmark System +Some rooms are visited frequently enough that it becomes a burden to search for them each time, for this we can use the bookmark system. + +When in a room you'd like to bookmark, you can do so with `db bookmark add here`. This will show you a message like: +``` +[speedwalk] Added a new bookmark for "Magic and Meddlers' Shop" in House of Magic - Creel (inside). ``` -#### Speedwalking -* Files: `src/maproute.py`, `src/gmcp.tin`, `src/db.tin` -* Route-finding logic and of course the database taken directly from [Quow's](http://quow.co.uk) lovely MUSHclient package. - -A python script (`src/maproute.py`) is handed the room_identifier of your current location and of your target location and attempts to create an alias to walk you there. This functionality can be invoked in-game with the `db route #` command, where `#` is the result number (0-9) that preceeds the results in any of the previously mentioned database search commands (`db item`, `db npc`, `db room`, `db gatherable`) - -**Example:** - -Here I am at Baldwin's smithy. But I need to make my way to the Mended Drum, let's speedwalk there (very contrived example but short and sweet). +You can list all saved bookmarks with `db bookmark list`, which will print a list of your saved bookmarks like so: ``` -[smithy] - @ - + -This is a small, cramped room with a large, roaring fire blazing in the forge on the east wall of the room. -There are various assorted tools of metalwork scattered around the fire and hanging on the walls, most notably -the anvil in the centre of the room. The smithy is obviously very busy as there is a large, dirty order book -lying open on a small desk near the door. -There is one obvious exit: south. -A cute goose, a young truffle pig, Baldwin "the Disemboweler" MacAvrik and Baldwin's apprentice are standing -here. -A piece of embroidered tartan is hanging on the wall and two iron chains and a copper short sword are on the -floor. - +[speedwalk] Speedwalk Saved Bookmarks: + [0]: Room "junction of the Avenue of the Pharaohs, Market Street and Phoenix Road" in Djelibeybi (outside). + [1]: Room "north end of Short Street outside the Mended Drum" in Ankh-Morpork (outside). + [2]: Room "beer room" in Ankh-Morpork (inside). + [3]: Room "Magic and Meddlers' Shop" in House of Magic - Creel (inside). ``` -First lets search for the Mended Drum -> `db room Mended Drum` +Generate a route to a listed bookmark by clicking on the number or room name (e.g. "2" or "beer room"), or typing `db bookmark route #`, this will show an output like: ``` -Results for rooms matching "Mended Drum". - [0]: north end of Short Street outside the Mended Drum (outside) found in Ankh-Morpork - [1]: entrance to the Mended Drum (inside) found in AM Buildings - [2]: bar of the Mended Drum (inside) found in AM Buildings - [3]: western part of the Mended Drum (inside) found in AM Buildings - [4]: toilets of the Mended Drum (inside) found in AM Buildings - [5]: cubicle in the toilets of the Mended Drum (inside) found in AM Buildings - [6]: cubicle in the toilets of the Mended Drum (inside) found in AM Buildings - [7]: landing on the first floor in the Mended Drum (inside) found in AM Buildings - [8]: front wall of the Mended Drum (outside) found in AM Buildings - [9]: roof of the Mended Drum (outside) found in AM Buildings +[speedwalk] Generating speedwalk from current location to specified room. +[speedwalk] A route was found, type speedwalk to fast travel. [190 steps] +``` +Now simply type `speedwalk` or left mouse-click on `speedwalk` to begin moving towards the specified room. + +Information about these commands can be viewed within TinTin++ by typing `db bookmark help`. + +### Updating Database +All room, item, npc and route data comes from [Quow's](http://quow.co.uk) database, which lives at `src/quow.db`. The latest version of this database can be pulled from Quow's MUSHClient packages at any time replace the existing file. + + +## Map Door Text Parsing +* Files: `src/mdtparse.py` (`src/gmcp.tin`, `src/mdt_matchcontrol.py`, `mdtconfig.json`, `logs/features/mdt_custom_matches.json`) + +Show what living things are in adjacent rooms that you have visibility into. Parses the prose sent by the game into a more easily readable format, sorts by most populated room first. +![Screenshot](https://atr0phy.net/discworld-tintin/mdt.png) + +### MDT Configuration +The `mdtconfig.json` file in the root of this package contains the baseline configuration. Things like "rats,cats,dogs,clouds" being worth 0 points, or "bodyguards" and "hoplites" being coloured orange. Make changes here you want to persist forever, assigning different point values to high-xp or good loot targets or mission item carriers like the "town crier". As this work is a blatant copy of [Quow's](http://quow.co.uk) excellent MUSHClient plugin, the file format for the configuration is identical. +### Temporary Targets +Sometimes it's useful to add a high-value target temporarily to the MDT configuration. Maybe you're looking for "poets" to deliver a letter to or an "Agatean ambassador" for a gold-trimmed obi delivery, or the pickpocket that robbed you earlier, this is possible with the `mdtc` commands (map-door-text-custom). + +Use `mdtc add ` to quickly add a NPC with a 5-point value and coloured red (e.g. `mdtc add Agatean ambassador`). + +Once you've tracked one down, use `mdt remove ` to remove it (e.g. `mdtc remove Agatean ambassador`). + +These custom matches are stored in their own file (`logs/features/mdt_custom_matches.json`) so there's no risk of removing entries from your baseline [configuration](#mdt-configuration) file, only those you've added specifically with the `mdtc add` command as described above. + +### MDT Window and Implementation Reference +The default `tmux` layout places a Map Door Text output window above the main game window and below the chat log. + +This pane runs the `bin/mdt` script which takes the raw log of map-door-text data that comes from GMCP and passes it to the `src/mdtparse.py` script to transform the output, then refreshes itself every half a second. The `src/mdt_matchcontrol.py` contains a few simple methods for the TinTin++ aliases to invoke to manipulate the `logs/features/mdt_custom_matches.json` file where temporary targets from `mdtc add ` are stored. + +## Mission Cooldown Timers +* File: `src/missions.tin` (`src/missions/sailing.tin`, `src/missions/jobmarket.tin`, `src/missions/letters.tin`, `src/missions/sausages.tin`) + +The mission cooldown functionality will track when you can do missions again across multiple characters for many of the repeatable missions available on DiscworldMUD. + +![Screenshot](https://atr0phy.net/discworld-tintin/mission_timers.png) + +This system currently supports the following missions: +* All AM Job Market missions (Mr Werks) +* All Dead Letter Office jobs (Frank) +* Easy and Hard Sausage Jobs (Sam Slager) +* Sailing Missions (Chidder/Smith) + +You can get instructions on using this system from within TinTin++ with the `missions help` command. + +### Missions Cooldown Report +Using the `missions` command will report all currently tracked mission timers for all characters, as seen in the screenshot above. + +### Timed Alerts +When a cooldown for a mission has ended, a coloured alert will be displayed on screen to inform you: + +![Screenshot](https://atr0phy.net/discworld-tintin/mission_alerts.png) + +### Save Across Restarts +Mission timers (like [spottimers](#hotspot-timers)) are saved to file every five minutes so that if you must restart your client you can restore your current mission timers and alerts afterwards. + +Use the `mtload` command to restore the latest snapshot from the filesystem. You can use the `mtsave` command to force a save before restarting. + + +## Chat and Talker Logging +* File: `src/channels.tin` + +The client will capture messages sent in `tells`, `group say`, `talker channels`, `club badges` as well as the (newbie) talker and others, and log them to file. This file can be displayed in a tmux pane to have a scrollable chat-history log as seen in the opening screenshot. + +![Screenshot](https://atr0phy.net/discworld-tintin/chat_log.png) + +### Adding New Channels / Clubs +In the `src/channels.tin` file you'll see examples for capturing Clubs and Talker channel chatter. You probably don't belong to my clubs so you can safely remove lines like the one for `(They Might Be Alts)` and add your own. Even the guild-level talkers are not all accounted for, but you can use my examples for (WIzards) or (Thieves) to add whatever you need. + +### Talker Channel Logging +By default messages on talker channels aren't logged to file by default, they can be noisy and often meaningless. However when there is an interesting conversation happening then logging to the chat file is useful. + +You can enable logging talker messages to the chat file with `talker log on` and stop logging talker messages with `talker log off`. If you'd like talker mesages to log by default unless/until you turn them off, simply change `#variable {talker_log_toggle} {0};` line in `src/channels.tin` to `#varaible {talker_log_toggle} {1};`. + +### Log File and Window Display +All logged chat and talker messages are stored in `logs/chat.log` which can be tailed in a tmux pane as seen in the default layout. + +## Session Logging +The entire TinTin++ session is logged to file every time you play, with a single log file per calendar day stored in the `logs/active` folder. This functionality is configured in the `config.tin` file. + +I occasionally go into this folder and compress historical logs from previous years into an archive to save data. + +## Mindspace Calculator +Many of the most useful features of [Quow's](http://quow.co.uk) mindspace plugin have been converted to TinTin++ and are available for use. This tracks your skills and spells-in-mind across multiple characters. + +### Spells Output and Gained/Lost +When you type `spells` to list the spells your character has in their head, each spell will be captured and stored against the characters name so that in the future we can show what spells you've gained or lost since last time (e.g. if you die and forget some spells). + +![Screenshot](https://atr0phy.net/discworld-tintin/mindspace_spells_gained.png) + +The client will also calculate the amount of mindspace used by spells both in total and per-category and report. Will also report how much `mindspace` you still have free for new spells if any, assuming you've run `mindspace skills` as described next. + +### Mindspace Skills Ingestion +Run the `mindspace skills` command to have the MUD output all your spell-related skill bonuses and your raw `magic.spells.special` bonus to calculate your mindspace and empower spellcheck results when looking up spells as described next. + +After the MUD outputs your skills the client will report back that it has set your characters total mindspace available: +``` +Mindspace max set to 440 (raw bonus + 30). ``` -Great, we got a list of results back. Let's aim for the `entrance to the Mended Drum`, which is result number `1` in our list. +### Spellcheck and Spell Lookup +You can lookup any spell by its acronym to find details about the spell, including: +* Mindspace and GP Cost +* Spell Category +* Spellbook and Location +* Components used and consumed -> `db route 1` +![Screenshot](https://atr0phy.net/discworld-tintin/mindspace_spellcheck.png) + +If you've ingested your skills with `mindspace skills` as described [here](#mindspace-skills-ingestion) then looking up a spell will also compare your skills to those needed to cast the spell and provide a spellcheck table as seen in the image above. + +### Spellbook Detail +When looking up a spells [details](#spellcheck-and-spell-lookup) you can left-mouse click on the title of the Book that spell is found in to get a list of other spells that are in that same book. + +![Screenshot](https://atr0phy.net/discworld-tintin/mindspace_book.png) + +Clicking the acronym / nickname of any of the listed spells will show spell [details](#spellcheck-and-spell-lookup) for that spell. + +### Orders Primaries Tracking +When possible your characters wizardly order is tracked and skills that are primaries will be labeled as such in the spellcheck output. + +![Screenshot](https://atr0phy.net/discworld-tintin/mindspace_primaries.png) + +In the above example as a Seer I get `binding`, `enchanting` and `evoking` as Primary Skills, hence the `*P*` designation. + +## Sailing Mission Helpers +* File: `src/missions/sailing.tin` + +The sailing helper includes a number of useful trackers, colour highlights and alerts for sailors. + +### Cooldown Tracking +Sailing is tracked like other missions with alerts and a report to track sailing cooldown for multiple characters, see [mission cooldown timers](#mission-cooldown-timers) for more. + +### Per-Mission XP and Duration Report +The total amount of time the sailing mission took and the total earned experience points are tracked each time you get on the boat. This information is automatically printed out when you land at port, and can be viewed again afterwards with the `sailreport` command: ``` -[speedwalk] Generating speedwalk from current location to entrance to the Mended Drum, AM Buildings... -[speedwalk] A route was found, type "speedwalk" to fast travel. -Changed alias "RuhsSpeedRun" from "nw;nw;w;nw;nw;sw;sw;sw $*$". - -alias RuhsSpeedRun -RuhsSpeedRun: s;e;e;n $*$ +[sail] Sailing Report: 655406 xp earned in 33 minutes and 58 second(s). ``` -We found a way there, and our speedwalking alias was updated. Now typing `speedwalk` would walk us over to the Mended Drum (s;e;e;n). - -#### Thiefey Things -* File: `src/quota.tin` - -Parses the output of 'read quota brief'. I recommend adding this to the .afterinventory alias of your thief characters so it kicks in right away. Has a number of different features: - -##### Status Bar Quota Time Left -Shows a HOURS:MINUTES countdown until your quota is due in the top bar near the TPA/XP monitors. This timer changes to a bright color when you near the final hour. - -##### Quota Flipping Alerts -A bright blue alert pops up an hour, thirty minutes and ten minutes before your quota flips over. There is a follow-up alert when your quota actually flips. On a per-session basis these alerts can be disabled with `quota alert off` and re-enabled with `quota alert on`. - -##### Left to Steal Calculation -When you 'read quota brief' a new line will be inserted calculating how much you have left to steal. - -#### Hunting Hotspot Timers (with Syncing!) -* File: `src/spottimers.tin` - -This set of configs is checking against a list of saved NPCs/Rooms (that I've determined, I think my hunting route is the bestest one, you might disagree, that's good for you) every time we see something die, or enter a new room, and records the time it happened. +You can also share these details with your group using the `gssail` command. +### Stage Tracking +Detects when you transition between phases of the sailing mission and reports these transitions to you (e.g. `calm`, `hail`, `fog`, `lightning`, `gale`). ``` -.:: Death Timers ::. - - (99m) delbert - - (11m) hlakket - - (???) stables - - (???) casino - - (???) rogues - - (???) grflx - - (32m) shaker lions - - (???) snail - - (72m) dojo - - (22m) medina boss - - (???) zoon liar - - (???) bandit leader - - (???) cguards - - (???) giant leader - - -.:: Visit Timers ::. - - (???) parades - - (05m) offler - - (09m) shades - - (37m) smugglers - - (???) black market +[sail] ### Hail Stage Begins ### +[sail] ### Calm Stage Begins ### +[sail] ### Gale Stage Begins ### ``` -Output like above is written to a file (so that it can be displayed next to the MUD, I keep it in a tintin pane above my ASCII map as you can see in the initial screenshot). - -Along with this come various commands to interact with the spot timers from within TinTin++ itself: - -##### List timers in-line -> `dt` - -The `dt` command will print the list of timers in the MUD output, allowing for them to be referenced even when not displaying the file output in real time. I make use of this when on mobile or having the MUD in a more compact window. - -##### Reset all or individual timers -> `dtreset all`, `dtreset hlakket` - -I've heard sometimes you can miss a spot or lose it to competition. Sounds like excuses to me, but just in case, you can note that you've lost a spot or otherwise know it went down by manually setting it's timer to "just now" with the `dtreset ` command. - -Similarly, people tell me that on occasion real life requires that they *stop* hunting for a while, letting any previously recorded timers go stale and become useless. In this scenario you can use `dtreset all` to return all spots to the `Unseen/???` status and start fresh. - -##### Share the timer list with your group -> `gsdt` - -On occasion you have to group with folks who don't use my hotspot timer for TinTin++ or MUSHclient, leaving you unable to sync with them. You can take pity on these poor souls by sharing a snapshot of your current timers with them via group chat with the `gsdt` command. - -``` -[groupname] Ruhsbaar: delbert: 99m | hlakket: 11m | stables: ??? | casino: ??? | rogues: ??? | grflx: - ??? | shaker lions: 32m | snail: ??? | dojo: 72m | medina boss: 22m | zoon liar: ??? | bandit leader: ??? - | cguards: ??? | giant leader: ??? | parades: ??? | offler: 05m | shades: 09m | smugglers: 37m | black - market: ??? -``` - -##### Sync timers to another player -> `dtsync ` - -It's often useful to share your timers with another player, if they are freshly joining the group and taking a lead, or maybe you want to be nice and share with someone before going to bed! This can be done with the `dtsync` command. - -The syncing is done via a `tell` in game, and works between both my tt++ and MUSH plugins, allowing you to sync between players using the other client. When a players syncs their timers to yours, the plugin compares them to your existing values and only takes updates (e.g. if someone who killed Hlakket 90 minutes ago syncs to you, but you killed Hlakket 11 minutes ago, your 11 minute timer will not be overwritten). - -Note that because the tell containing sync information is full of lengthy unix timestamps, you'll need to make sure to set your `cols 999` in-game before having another player sync to you (this is only needed on tt++, MUSH handles this gracefully). +### Sail Colour Highlights +Many important lines throughout the sailing missions are coloured to draw your attention to them, some of which include: +* Monster appearing (kraken or serpent) and leaving +* Ship begins moving or halts +* Overboard when dragged by **rope** or **swimming** along +* Serpent lunging, Kraken coiling, etc +* Fires in adjacent rooms or currnet room +* Ice and seaweed interactions +* Boiler engine dial output +* Dragon hunger/boredom levels and toy/food interactions +* Tying yourself, others, crates +* Frayed levels of ropes you look at +* Caught in forward/backwards current +* Caught in whirlpool +* Knocked or gusted off-course +* Sextant star/sun highlights -#### Group Shield Monitor +## Group Features * File: `src/group.tin` -Actions/Triggers capture the shield-status of your groupmates and keeps track of who is protected by what. These get written to a file in grid-form for display in the UI and are also displayed by command in the regular output. +A number of features related to grouping to track group member shield status and recall where floaters have dropped, or weapons were snatched by muggers etc. -##### See group shields -> `sgs` -Shows the grid of allies and their shields in the MUD window. I use this when on mobile/small-terminal and don't have the grid up in its own pane - useful because its unaffected by queued commands unlike a fresh "group shields". +### Group Shield Monitor +Tracks the status of your shields and group members shields in "real-time", accounting for `TPA`, `MKII`, `EFF`, `GSHG`, `CCC` and `MS`. Both EFF and GSHG (the witches Household Guard equivalent) are tracked together under the `EFF` label. -##### Reset group shields -> `rgs` -Clears out all current group shield data, and then runs "group shields" again to refresh itself. The system isn't perfect, if something is wonky, use this to get a fresh start. +![Screenshot](https://atr0phy.net/discworld-tintin/group_shields.png) + +*TODO*: need a good group shields window screenshot + +#### Commands +The following commands are available: +* `sgs` (show group shields) will display a grid of group mates and shields (like the screenshot above) +* `rgs` (reset group shields) will clear all data and run `group shields` to refresh itself + +#### Window / Display File +The output you'd see when using the `sgs` (show group shields) command is also written to a file every time a shield update is processed at `/logs/groupshields.log`. You can use the shell script at `bin/grp` to display this file in a window. The default tmux configuration that comes with this package does this already. + +### TPA and EFF Alerts +For TPA anytime yours or a groupmates breaks the output line from the MUD will be coloured brightly, logged to the chat window and a terminal bell will be rung. + +For EFF the same colouring, chat logging and terminal bell will happen, however you can disable terminal bells for others EFF drops (not yours) with `gs eff off`, and re-enable it with `gs eff on` - this is not a persistent setting, alerts will default to on each session. + +### Group EFF Drop Tracker +When a groupmates EFF fell down and no one noticed you can help them find it with the `gseff` command to report to group chat the location where we last registered a EFF fall out of orbit. +``` +[kf] Ruhsbaar: [eff] Deramius's floating "the black iron shield" was dropped in {on the surface of a large pond} +``` + +### Group Weapon Snatch Tracker +When a groupmate has their weapon snatched by a Shades mugger or otherwise you can help them find out who exactly stole it with the `gssnatch` command to report to group chat the details of the last weapon-snatch we've seen. +``` +[/dev/nli] Ruhsbaar: [snatch] Goody Gnicky's "a bronze javelin" was stolen by {scary mugger (hiding)} +``` + +## XP Tracker +The amount of experience you've earned total is tracked for the length of the TinTin++ session (and optionally for smaller windows as explained [below](#windows)). It tracks properly through SUs/character switching and is displayed in the [prompt bar](#tintin-prompt-bar) and can be reported to the group with the `gsxp` command. +``` +[kadfja] Ruhsbaar: 21,860xp in 0h 2m (655k/h) +``` + +You can view a report of XP earned and rate for the current window and entire session with the `xpreport` command: +``` +=><= XP Report =><= +Window: 21,737xp total in 0h 1m @ 1,304k/h +Session: 1,412,414xp total in 7h 24m @ 190k/h +``` + +### Windows +When hunting it can be useful to track the XP earned and rate from when you started leading, on top of the overall session. This can be accomplished by starting a new "tracking window" with the `xpreset` command. Using this will start a new timer and begin tracking XP exclusively since it began, changing what's shown in the prompt and what's reported with the `gsxp` command. + +For example say I was 7 hours into my play session so my [prompt bar](#tintin-prompt-bar) showed `1,389,768xp in 7h 13m (192k/h)`, I could enter `xpreset` and it would then show `0xp in 0h 0m (0k/h)` ready to start tracking anew from that moment on. + +Regardless of how many times you `xpreset` the total gains for the session are always tracked seperately and can be reported with `gsxp all` + +### Commands +A few commands are available for interfacing with the XP tracker: +* `xpreset` to start a new tracking window +* `xpreport` to show yourself the XP gained and rate for current window and entire session +* `gsxp` to report current window xp rate to the group +* `gsxp all` to report current window and whole session xp rate to the group + +## Hotspot Timers +* File: `src/spottimers.tin` (`src/gmcp.tin`) + +Tracks the time since you've last seen an NPC die or visited a room based on a list configured in `src/spottimers.tin`. You can adjust the room-ids or NPC name patterns to track in the top of that file. You can display the current state of timers with the `dt` command, or [display them in a tmux pane](#window--file-display). + +![Screenshot](https://atr0phy.net/discworld-tintin/spottimers.png) + +### Window / File Display +One of the tmux panes in the standard layout is set to run `bin/dt` which outputs the contents of `logs/xptimers.log` which holds the same contents as you'd see from typing `dt`. + +### Reset Spots +Sometimes your timers are stale or old and need to be reset. + +#### Reset A Spot +The other group beat you to it, shame on you, but you still need to track a spot like it was just killed. Pass the display name of the spot to the `dtreset` command to set it as if it were just killed/visited. + +Examples: +* `dtreset delbert` +* `dtreset zoon liar` +* `dtreset bmarket` + +#### Reset All Spots +If you gave up hunting a while to reset your heartbeat and all your spottimers are old or stale you can reset all spots to their `unseen` state as if you'd just booted up using `dtreset all`. + +### Sync With Others +When hunting with other folks who use discworld-tintin (or my MUSHClient hotspot timer plugin) can sync timers between each others clients via tell by using the `dtsync ` command. For example if I want to send my hotspot timers to Ruhsbaar because he just joined the group I can do `dtsync Ruhsbaar` to send them over. + +### Share With Group +You can display a snapshot of your hotspot timers to group chat using the `gsdt` command (group-say-death-timers). +``` +[adgfj] Ruhsbaar: hlakket: 06m | rogues: 08m | offler: 03m | grflx: 08m | medina boss: 99m | zoon liar: 08m | dragon: 28m | offler: 03m | shades: 99m | cabbage: 53m | smugglers: 99m | +``` + +### Colouring +Each spot defined in the configuration file is given a `respawn` value which is meant to be an integer number of minutes. Ten minutes before and five minutes after the specified time the spot will show as **yellow** colour, then show as **red** for up to twenty minutes after that, and back to normal color. + +For example if I say a spot is going to respawn in 60 minutes - and then go kill it: +* After 50 minutes (60 - 10) it will show as yellow +* After 65 minutes (60 + 5) it will show as red +* After 80 minutes (60 + 20) it will go back to grey + +### Save Across Restarts +Every five minutes, and anytime the client is either disconnected from the MUD or quit from, the state of your hotspot timers are saved to a file. When reloading TinTin++ you can use the `dtload` command to load them from file so you don't lose your timers. You can force a save to file before restarting with the `dtsave` command. -### Highlights/Actions/Other -* HP loss and XP gain notifications +## TinTin Prompt Bar +* Files: `src/prompt.tin` (`src/xpmonitor.tin`, `src/tpamonitor.tin`, `src/quota.tin`) + +The prompt bar is the #split box drawn at the top of the TinTin++ window, used to display all sorts of meaningful information for your current DiscworldMUD session. +![Screenshot](https://atr0phy.net/discworld-tintin/prompt_bar.png) + +### TPA Monitor +The first section of the prompt bar monitors your characters current TPA (Transcendent Pneumatic Alleviator) shield status. If you have a TPA active it will show "TPA" in white letters (updated when cast, or by the output of `shields` or `group shields`). As your TPA shield takes damage, the colouring of the "TPA" indicator in the prompt bar will update to give you a real-time understanding of the state of your shield. It will turn dull-red, bright-red, orange and yellow as appropriate. + +If your shield actually breaks the message will be highlighted as bold red and underlined, logged to the chat window and the prompt-bar indicator will disappear until re-cast. The client also tracks the **length** of your TPA - the number of minutes between it being cast and breaking - this will be outputted above the shield-break line as shown below. + +![Screenshot](https://atr0phy.net/discworld-tintin/tpa_timer.png) + +### Experience Gain Display +The next section of the prompt bar will show how long the session has been running (or time since the last `xpreset`), the total XP earned during that time and your XP-per-hour rate. + +For example: `1,207,957xp in 2h 41m (450k/h)` telling me it's been 2h 41m since I logged in (or last used `xpreset`), in that time I've earned 1,207,957xp total, which calculates out to about 450,000xp per hour. + +For more information on XP gain tracking, using `xpreset`, sharing the rate with the group and other options see the [XP Tracker](#xp-tracker) section. + +### Thief Quota Timer +If your current character is a thief from Ankh-Morpork and you've run `read quota brief` the [Thief Quota Helper](#thief-quota-helper) will kick in to track your remaining quota period and more, see that section for details. + +The time you have left on your quota period will be displayed in the prompt bar, and color magenta when you're an hour or less away from flipping over. + +![Screenshot](https://atr0phy.net/discworld-tintin/prompt_quota_display.png) + +### Current Local Time +Finally to the very right-hand side of the prompt bar the system time will be displayed in 12-hour am/pm format. + +## Thief Quota Helper +* Files: `src/quota.tin` + +The quota helper reads the output of `read quota brief` to track the time left until your quota flips over and calculate a A$ value left to steal to hit quota maximum. I recommend adding `read quota brief` to your `.afterinventory` alias in-game so that anytime you login as or SU to a thief character you'll be automatically set to track. + +### Left to Steal Calculation +When you `read quota brief` an extra line will be added to tell you how much more you can steal before hitting your maximum. +``` +Inventory regeneration complete. + Receipts received: A$137.50. + Guild cut: A$41.25. + Amount paid: A$0. + Amount owed: A$41.25. + Time left: 4 hours, 19 minutes, 41 seconds. + Min quota: A$53. + Left To Steal: A$174.500000 <---- this one + Max quota: A$312. + Max left to steal: A$174.50. +You briefly consult the brown leather Thieves' Guild licence. +``` + +### Quota Flip Alerts +Once the client is tracking your remaining quota time from `read quota brief` it sets alerts to remind you `an hour`, `half an hour` and `ten minutes` before your quota flips over, and again when it resets, like so: + +![Screenshot](https://atr0phy.net/discworld-tintin/quota_alerts.png) + +These alerts are enabled-by-default on client start, but can be turned off for the session using `quota alert off` (and turned on again with `quota alert on`). + +### Prompt Bar Timer +A countdown of time left until your quota flips will be displayed in the [prompt bar](#thief-quota-timer). + +## Other Colour Highlights and Triggers +* Combat highlighting + * Specials (incoming/outgoing/other) + * Backstabs + * Base attack rounds +* Magic highlighting + * Delude levels and deepens/fade messages + * Item enchantment levels + * Background magic levels + * Pragi fire damage percentages + * Beautified spell casting + * Coloured FCI output + * Library and distortion highlights + * Contemplate colouring + * Djinn weapon invoke colouring + * Balsa wand zapping + * TPA and EFF drops +* Travelling shop materializing +* Incident highlighting + * Weapon snatches and snatched weapon drops + * Rumbling JPCT doors + * You and others fumbling inventory items + * Tossed by stone giants +* `count` command highlighting +* Gemcutting highlights * Shelox fight highlights -* TPA shield drops -* T-Shop rooms -* Snatch/Filch -* Rumbling doors -* Inventory fumbles -* Copperhead mines gases -* Per day separated log files -* More... - -### Layouts -The "Discworld" shell script will configure a slim (mobile friendly) layout when run with no arguments. This adds a split for chat and MDT stacked above the MUD window itself. If run with "full" as an argument, the timers, shield monitor, ascii-map will be off to the right hand side. If you are already inside of the "discworld" tmux session, running "Discworld" will re-arrange the layout as specified for easy switching between desktop and mobile. +* Copperhead mines highlights + * Crate game colouring + * Gem vein wall assess colouring + * Purple mineral powder pannning colouring + * Dampgas bell alerts and highlighting -## Credit Where It's Due -I've spent a lot of time and effort on this setup to make it perfect for my use and the contents herein have all been touched or modified by me in some way if not created outright, but I've been inspired by and blatantly stolen bits of it from two folks in particular: +# Tmux Setup +The "Discworld" shell script either creates a new or attaches to an existing tmux session named `discworld`. When creating a session it uses [tmuxp](https://github.com/tmux-python/tmuxp) to create the layout and run initial commands for each pane. The layout definition for `tmuxp` is in the `discworld.yaml` file. + +I recommend sym-linking the Discworld script to some place in your $PATH as 'disc' so that at anytime you can type `disc` into a terminal and create or take-over your DiscworldMUD session, for example: +``` +sudo ln -s /home/myuser/discworld-tintin/Discworld /usr/bin/disc +``` + +## Toggle Full or Mobile View +When connected via mobile device its convenient to collapse the right-hand side bar, hiding the spot-timers, group shields and minimap panes to view the MUD full-width when in portrait orientation. The `Discworld` script configures two key bindings to quickly make this adjustment: +* `Ctrl-F` - toggle to full-view (sidebar visible) +* `Ctrl-G` - toggle to mobile-view (sidebar collapsed) + +Here's an example of full-view on mobile: + +![Screenshot](https://atr0phy.net/discworld-tintin/mobile_full.png) + +And the collapsed view on mobile: + +![Screenshot](https://atr0phy.net/discworld-tintin/mobile_collapsed.png) + + +# Mouse Support +The latest versions of TinTin++ have expanded mouse support, which has been implemented in the following ways. + +## Scrolling Buffer +The buffer or backlog of output from the MUD is now controlled by TinTin++ and not Tmux, as such scrolling up or down with the mouse-wheel will be picked up by TinTin++ and move your view of the buffer up or down by five lines at a time. + +## Input Bar +The input bar can be left-click in with the mouse to set the position of the cursor for editing. + +## Clickable Links +The MSLP protocol is used to create clickable links in the output to send further commands. This is used throughout the [database search](#speedwalking-and-roomnpcitem-search) and [mindspace](#mindspace-calculator) functionality. + +# Command Reference +A condensed listing of all the commands/aliases that this setup provides. + +## XP Tracker Commands +* `xpreset` - reset xp rate and start new window +* `xpreport` - report current window and whole session XP +* `gsxp` - report current xp gains and rate to group +* `gsxp all` - report current and whole session XP rate to group + +## Hotspot Timer Commands +* `dt` - show spot timers report +* `dt help` - show spot tiemrs help file +* `dtreset ` - reset all spots or a particular spot +* `dtsave` - save hotspot timers to file +* `dtload` - load hotspot timers from file +* `gsdt` - report hotspot timers to group + +## Mission Commands +* `missions` - show active mission timers for all characters +* `missions help` - show missions help file +* `gssail` - report last sail length and XP to group +* `sailreport` - show sailing report +* `mtsave` - save mission timers to file +* `mtload` - load mission timers from file + +## Group Commands +* `rgs` - reset group shields data +* `sgs` - show group shields report +* `gs eff ` - enable or disable groupmate EFF drop alerts +* `gssnatch` - report last seen weapon snatch to group +* `gseff` - report last seen EFF drop to group +* `gssail` - report last sail length and XP to group +* `gsdt` - report hotspot timers to group +* `gsxp` - report current xp gains and rate to group +* `gsxp all` - report current and whole session XP rate to group + +## Database Search and Speedwalk Commands +* `db gatherable ` - search for gatherable items +* `db npc ` - search for npcs +* `db npcitem ` - search npc inventories for item +* `db item ` - search for a sold item +* `db room ` - search for a room by name +* `db route <#>` - route to a search query result +* `db route ` - route to a room by id +* `speedwalk` - run the speedwalk alias (after routing) +* `db` - points to `db help` +* `db help` - show database command helpfile +* `db bookmark help` - show bookmark room helpfile +* `db bookmark list` - show list of saved / bookmarked rooms +* `db bookmark add here` - add current room to bookmark list +* `db bookmark remove <#>` - remove a saved bookmark +* `db bookmark r oute <#>` - route to a saved bookmark room + +## Talker and Chat Commands +* `talker log on` - enable logging talker messages to chat window +* `talker log off` - disable logging talker messages to chat log + +## Mindspace Commands +* `mindspace skills` - capture skills for this character for spellcheck/mindspace +* `mindspace help` - show mindspace help file +* `mindspace ` - show spell detail and spellcheck for a spell (e.g. "jpct") +* `spells` - report free mindspace and spells gained/lost + +## Quota Alert Commands +* `quota alert ` - turn quota flip reminders on or off + +## Map Door Text Commands +* `mdtc add ` - add a temporary map-door-text match +* `mdtc remove ` - remove a temporary map-door-text match + +## Utility Commands +* `sotp` - catches misspelling of `stop`, sends `stop` +* `colourpalette` - shows all <rgb> colors possible with tt++ +* `minesolve %1 %2 %3` - calculates how many hammer chips/pickaxe strikes to mine a gem vein in copperhead +* `debug ` - show GMCP messages in output for debugging + +## Sizing Commands +* `setsize` - sets rows to 300 and cols to 999 + +The following commands set cols to 60, send the command to the MUD, then resize to cols 999 +* `skills` +* `speak` / `language` / `languages` +* `spells` +* `rituals` +* `group status` +* `who` +* `help` +* `nickname` + +# Credits +I've spent a lot of time and effort on this setup to make it "perfect" for my use and the contents herein have all been touched by or modified by me in some way if not created outright, but I've been inspired by and blatantly taken bits from some folks in particular: ### Quow -You don't play this game without knowing who Quow is and at least having heard about all the work he's done on both various extensions for MUSHClient as well as the immense data collection effort that resulted in his database of rooms, npcs, items and more. +You don't play the game without knowing Quow and his **outstanding** work on his MUSHClient plugins for the game, which are far-and-away the most advanced. If you don't have a reason to use TinTin++ you should really go use his setup instead available at http://quow.co.uk. -The "mapdoortext" and "speedwalking" functionality present in my TinTin++ configurations is merely a port of the Lua code that exists in his plugins that I copied to and modified to work in my terminal-only environment. - -You can find Quow's work on his website: +The "map door text" parsing, "speedwalking" and database search, and "mindspace" functionality present in this repository are nothing more than tintin++/python ports of Quows work. ### Oki -I've never even met Oki in game, but I stumbled upon their repository of tintin scripts some time ago and happily copied some of their magic/combat substitutions and highlights for my own use. +I've never even met Oki in game, but I stumbled upon their repository of TinTin++ scripts some time ago and happily copied some of their magic/combat substitutions and quota triggers. You can find Oki's repository here: https://git.tubul.net/richard/tt_dw.git -You can find Oki's repository here: +### Dilbo and Kiki +The player Dilbo shared a bunch of Sailing highlights with me that I believe were taken from the player Kiki's MUSHClient sailing plugin, so thanks to both of them! +# Disclaimer +The files in this repository were put together for my own personal use and come with no guarantee to work for you or your fairy godmother. I provide these files "as-is" and any content herein that is my own is released under the terms and conditions of the [GNU Affero General Public Licence v3.0](https://www.gnu.org/licenses/agpl-3.0.en.html). -## Disclaimer -The files in this repository were created/modified/copied by me for my own personal use and come with no guarantee to work for you or your fairy godmother. I provide these files "as-is" and offer no support whatsoever to get them working. -Any content herein that is my own is released under the terms and conditions of the [GNU Affero General Public Licence v3.0](https://www.gnu.org/licenses/agpl-3.0.en.html) +If you need help getting setup, have questions about how things work, feature request, etc - feel free to reach out to me in game and I'm happy to discuss, but make no promises whatsoever. +Happy MUDing <3 diff --git a/bin/mdt b/bin/mdt index 3e87fe4..4004757 100755 --- a/bin/mdt +++ b/bin/mdt @@ -3,7 +3,7 @@ # clear && watch --color -t -n 0.4 'python src/mdtparse.py logs/mapdoortext.log | tail -n `tput lines`' clear && while true; do - output=$(printf "\ec"; python src/mdtparse.py logs/mapdoortext.log | tail -n `tput lines`); + output=$(printf "\ec"; python3 src/mdtparse.py logs/mapdoortext.log | tail -n `tput lines`); echo "$output"; sleep 0.5; done diff --git a/config.tin b/config.tin index 770cb91..f2f8e56 100644 --- a/config.tin +++ b/config.tin @@ -1,15 +1,15 @@ #VARIABLE {chat_file} {logs/chat.log} #CONFIG {SPEEDWALK} {OFF}; -#CONFIG {SPLIT}; -#CONFIG {PACKET PATCH} {5}; +#CONFIG {PACKET PATCH} {0.75}; #nop not perfect, yet to find a value that stops all broken lines; #CONFIG {COLOR PATCH} {ON}; #CONFIG {MCCP} {ON}; +#CONFIG {MOUSE} {ON}; #CONFIG {LOG} {RAW}; -#CONFIG {BUFFER SIZE} {2000000}; +#CONFIG {BUFFER SIZE} {1000000}; #config {COMMAND COLOR} {}; -#SPLIT {2} {1}; +#SPLIT {3}; #nop removed arg: {1} and removed #CONFIG {SPLIT}; #READ {src/channels.tin}; #READ {src/actions.tin}; @@ -24,18 +24,53 @@ #READ {src/quota.tin}; #READ {src/combat.tin}; -#READ {src/deadletter.tin}; #READ {src/db.tin}; #READ {src/magic.tin}; -#READ {src/missions.tin}; +#READ {src/missions.tin}; #nop this file loads all the individual mission files (sailing, sausage, werks); #READ {src/group.tin}; +#READ {src/mindspace/mindspace.tin}; + +#READ {src/personal.tin}; + #SESSION {discworld} {discworld.starturtle.net} {4242}; +#nop #SESSION {discworld} {disctemp.starturtle.net} {23}; #FORMAT {log_file} {logs/active/%t.log} {%Y-%m-%d}; #LOG {APPEND} {$log_file}; -#ACTION {Greco the Departure Gecko waves.} {#end} +#HISTORY {read} {logs/history.log}; + +#EVENT {SESSION DISCONNECTED} { + /writetimerstosyncfile; + /writemissiontimerstofile; + #HISTORY {write} {logs/history.log}; +}; +#EVENT {SIGHUB} { + /writetimerstosyncfile; + /writemissiontimerstofile; + #HISTORY {write} {logs/history.log}; +}; +#TICKER {timers_to_file} { + /writetimerstosyncfile; + /writemissiontimerstofile; + #HISTORY {write} {logs/history.log}; +} {60}; + +#EVENT {SCROLLED MOUSE WHEEL UP} {#buffer up 5}; +#EVENT {SCROLLED MOUSE WHEEL DOWN} {#buffer down 5}; + +#FUNCTION {log_line_to_chat} { + #script {ts} {date +"%I:%M%P"}; + #line {log} {$chat_file} {$ts[1] \}; + #line {log} {$chat_file}; +}; + + +#EVENT {SHORT-CLICKED MOUSE BUTTON ONE -1} +{ + #cursor position %1 +} #FORMAT {start_time} {%T}; diff --git a/discworld.yaml b/discworld.yaml new file mode 100644 index 0000000..748003d --- /dev/null +++ b/discworld.yaml @@ -0,0 +1,16 @@ +--- +session_name: discworld +windows: + - focus: 'true' + layout: 459d,144x79,0,0[144x8,0,0,289,144x10,0,9,290,144x59,0,20{116x59,0,20,291,27x59,117,20[27x38,117,20,292,27x10,117,59,293,27x9,117,70,294]}] + options: {} + panes: + - tail + - sh + - focus: 'true' + shell_command: tt++ + - bash + - sh + - bash + start_directory: "$(dirname '$0')" + window_name: DiscworldMUD diff --git a/logs/active/.gitignore b/logs/active/.gitignore new file mode 100644 index 0000000..f935021 --- /dev/null +++ b/logs/active/.gitignore @@ -0,0 +1 @@ +!.gitignore diff --git a/logs/chat.log b/logs/chat.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/features/dbbookmarksync.tin b/logs/features/dbbookmarksync.tin new file mode 100644 index 0000000..f393085 --- /dev/null +++ b/logs/features/dbbookmarksync.tin @@ -0,0 +1,7 @@ +#CLASS {dbsync} OPEN + +#VARIABLE {bookmark_sync} +{ +} + +#CLASS {dbsync} CLOSE diff --git a/logs/features/mdt_custom_matches.json b/logs/features/mdt_custom_matches.json new file mode 100644 index 0000000..b6dbe94 --- /dev/null +++ b/logs/features/mdt_custom_matches.json @@ -0,0 +1 @@ +[["town crier", "red", 5, false]] diff --git a/logs/features/mindspace_sync.tin b/logs/features/mindspace_sync.tin new file mode 100644 index 0000000..e7ec56c --- /dev/null +++ b/logs/features/mindspace_sync.tin @@ -0,0 +1,7 @@ +#CLASS {mindsync} OPEN + +#VARIABLE {mindspace_sync} +{ +} + +#CLASS {mindsync} CLOSE diff --git a/logs/features/missiontimers_sync.tin b/logs/features/missiontimers_sync.tin new file mode 100644 index 0000000..6c83d02 --- /dev/null +++ b/logs/features/missiontimers_sync.tin @@ -0,0 +1,27 @@ +#CLASS {mtsync} OPEN + +#VARIABLE {missions_sync} +{ + {jobmarket} + { + {board_codes} {} + {current_code} {0} + {current_name} {0} + } + {letter} + { + {last_difficulty} {0} + } + {sailing} + { + {active} {0} + {experience} {0} + } + {sausage} + { + {current_difficulty} {1} + {current_primer} {1} + } +} + +#CLASS {mtsync} CLOSE diff --git a/logs/features/xptimers_sync.tin b/logs/features/xptimers_sync.tin new file mode 100644 index 0000000..1143b27 --- /dev/null +++ b/logs/features/xptimers_sync.tin @@ -0,0 +1,311 @@ +#CLASS {dtsync} OPEN + +#VARIABLE {killtimers_sync} +{ + {Charles Marchella} + { + {group} {true} + {name} {casino} + {respawn} {40} + {time} {1770772056} + } + {Delbert} + { + {group} {false} + {name} {delbert} + {respawn} {40} + {time} {1770771775} + } + {Enrico Persuica} + { + {group} {true} + {name} {stables} + {respawn} {40} + {time} {0} + } + {Frankie Harvard} + { + {group} {true} + {name} {casino} + {respawn} {40} + {time} {1770772051} + } + {Geraldo Ciaco} + { + {group} {true} + {name} {casino} + {respawn} {40} + {time} {1770772043} + } + {Hlakket the Bartender} + { + {group} {false} + {name} {hlakket} + {respawn} {40} + {time} {1770770657} + } + {Joe Corrola} + { + {group} {true} + {name} {stables} + {respawn} {40} + {time} {0} + } + {Louis Accardo} + { + {group} {true} + {name} {stables} + {respawn} {40} + {time} {0} + } + {Marlon Gumboni} + { + {group} {true} + {name} {casino} + {respawn} {40} + {time} {1770772078} + } + {Samuel Casso} + { + {group} {true} + {name} {stables} + {respawn} {40} + {time} {0} + } + {Slim Stevie} + { + {group} {false} + {name} {rogues} + {respawn} {40} + {time} {0} + } + {the %* crocodile} + { + {group} {false} + {name} {offler} + {respawn} {40} + {time} {1770771089} + } + {the %* desert nomad} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } + {the %* grflx worker} + { + {group} {false} + {name} {grflx} + {respawn} {40} + {time} {0} + } + {the %* hermit} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } + {the %* lion} + { + {group} {false} + {name} {shaker} + {respawn} {40} + {time} {1770771120} + } + {the %* mystic} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } + {the %* nitsuni} + { + {group} {true} + {name} {snail} + {respawn} {40} + {time} {0} + } + {the %* patroller} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } + {the %* rujona} + { + {group} {true} + {name} {snail} + {respawn} {40} + {time} {0} + } + {the %* student} + { + {group} {false} + {name} {dojo} + {respawn} {40} + {time} {0} + } + {the Ome outlaw} + { + {group} {true} + {name} {snail} + {respawn} {40} + {time} {0} + } + {the Triad boss} + { + {group} {false} + {name} {medina boss} + {respawn} {40} + {time} {1770770069} + } + {the Zoon Liar} + { + {group} {false} + {name} {zoon liar} + {respawn} {40} + {time} {0} + } + {the bandit leader} + { + {group} {false} + {name} {bandits} + {respawn} {40} + {time} {1770771258} + } + {the ceremonial guard} + { + {group} {false} + {name} {cguards} + {respawn} {40} + {time} {1770771023} + } + {the citadel guard} + { + {group} {false} + {name} {citguard} + {respawn} {40} + {time} {1770770873} + } + {the giant leader} + { + {group} {false} + {name} {giants} + {respawn} {40} + {time} {0} + } + {the peacekeeper commander} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } + {the smuggler captain} + { + {group} {false} + {name} {smug cap} + {respawn} {40} + {time} {1770771880} + } + {the {Tang|Fang|Sung|Hong|McSweeney} captain} + { + {group} {true} + {name} {bmarket} + {respawn} {40} + {time} {1770770324} + } + {the {strict|serious|grim|tough|stern} captain} + { + {group} {true} + {name} {oasis} + {respawn} {40} + {time} {0} + } +} +#VARIABLE {visittimers_sync} +{ + {0ae9d8c4f2b48f4fcc4cb57fd85443399be2ecc5} + { + {group} {true} + {name} {bmarket} + {respawn} {40} + {time} {0} + } + {2950a174f65ef8e38eee444295c9b4832d36ab60} + { + {group} {false} + {name} {dragon} + {respawn} {40} + {time} {1770771696} + } + {3862c5fd0fe83f4080bdb9b94519bf8da89d5015} + { + {group} {true} + {name} {parades} + {respawn} {40} + {time} {1770772136} + } + {70546ec71867645ab5c51e9ce6087b75dcf4176f} + { + {group} {false} + {name} {offler} + {respawn} {40} + {time} {1770771072} + } + {7377f4483763eca72e0b97a87f71c5266e33f2cd} + { + {group} {false} + {name} {cocoons} + {respawn} {40} + {time} {1770771126} + } + {AMShades} + { + {group} {false} + {name} {shades} + {respawn} {20} + {time} {1770771752} + } + {c7c6f5720d2c587f80696b6f0ec7f9ac0f084b06} + { + {group} {true} + {name} {bmarket} + {respawn} {40} + {time} {1770770328} + } + {cca9645565f2bb0d017ea9b1dcc9d1b3e9fcfe72} + { + {group} {false} + {name} {cabbage} + {respawn} {40} + {time} {0} + } + {e4646eab906aef1187a9dd3e980441b8f6b8d68c} + { + {group} {true} + {name} {bmarket} + {respawn} {40} + {time} {1770770282} + } + {ebff897af2b8bb6800a9a8636143099d0714be07} + { + {group} {false} + {name} {smugglers} + {respawn} {20} + {time} {1770771920} + } + {f6c60dd36ed38707eb954c3a670f2f9370a79399} + { + {group} {true} + {name} {bmarket} + {respawn} {40} + {time} {1770770284} + } +} + +#CLASS {dtsync} CLOSE diff --git a/logs/groupshields.log b/logs/groupshields.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/history.log b/logs/history.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/mapdoortext.log b/logs/mapdoortext.log new file mode 100644 index 0000000..f387a0f --- /dev/null +++ b/logs/mapdoortext.log @@ -0,0 +1 @@ +A door east of here and the limit of your vision is here. diff --git a/logs/minimap.log b/logs/minimap.log new file mode 100644 index 0000000..6bb8dcb --- /dev/null +++ b/logs/minimap.log @@ -0,0 +1,2 @@ + @+ + diff --git a/logs/xptimers.log b/logs/xptimers.log new file mode 100644 index 0000000..e69de29 diff --git a/mdtconfig.json b/mdtconfig.json index 83b488f..cde6fa4 100644 --- a/mdtconfig.json +++ b/mdtconfig.json @@ -52,9 +52,11 @@ ["Ramtops Highlights"], ["headless horse rider", "red", 8, false], + ["wandering trollish locksmith", "red", 5, false], ["Ankh-Morpork Highlights"], ["barbarian", "red", 5, false], + ["watchman", "red", 5, false], ["Genua Highlights"], ["Tfat Chick", "red", 10, false], diff --git a/src/actions.tin b/src/actions.tin index 090c992..eb98ed9 100644 --- a/src/actions.tin +++ b/src/actions.tin @@ -1,32 +1,42 @@ #CLASS {actions} {kill}; #CLASS {actions} {open}; -#NOP ==[TPA Alerts]== -#HIGHLIGHT {^There is a sudden white flash. Your magical shield has broken.$} {bold red underscore}; -#HIGHLIGHT {^There is a sudden white flash around {(?!a passing wizard).+}.$} {bold red underscore}; +#NOP ==[TPA Alerts]==; +#HIGHLIGHT {^There is a sudden white flash. Your magical shield has broken.$} {Red underscore}; +#HIGHLIGHT {^There is a sudden white flash around {(?!a passing wizard).+}.$} {Red underscore}; #ACTION {^There is a sudden white flash around {(?!a passing wizard).+}.$} { #showme {\a}; - #line {log} {$chat_file}; + #return @log_line_to_chat{}; }; +#NOP ==[EFF Alerts]==; +#SUB {^In blocking the attack {(?:.+)} floating around {(?:\w+\b\W*?){1,8}} is knocked out of orbit.$} {<199><438>In blocking the attack <788>%1<099><188><438> floating around <788>%2<099><188><438> is knocked out of orbit.<099>}; + #NOP ==[T-Shop Alerts]== -#HIGHLIGHT {Greasy sparks crawl over the %1 wall, and a door silently materialises.} {bold green underscore}; -#HIGHLIGHT {There is a doorway in the %1 wall leading into a curious shop.} {bold green underscore}; +#HIGHLIGHT {^Greasy sparks crawl over the %1 wall, and a door silently materialises.$} {green underscore}; +#HIGHLIGHT {^There is a doorway in the %1 wall leading into a curious shop.$} {green underscore}; +#ACTION {^Greasy sparks crawl over the %1 wall, and a door silently materialises.$} { #showme {\a}; }; +#ACTION {^There is a doorway in the %1 wall leading into a curious shop.$} { #showme {\a}; }; #NOP ==[Incident Highlighting]== -#HIGHLIGHT {^%1 grabs %2 from you. You struggle briefly but %3 wrests it from your grip and makes for a hasty retreat.$} {bold red underscore}; -#HIGHLIGHT {^%1 grabs {his|her|its} %2. You struggle briefly but {he|she|it} wrests it from your grip and makes for a hasty retreat.} {bold red underscore}; -#HIGHLIGHT {^%1 grabs your %2. You struggle briefly but {she|he|it} wrests it from your grip and makes for a hasty retreat.} {bold red underscore}; -#HIGHLIGHT {^As %1 dies %2 drops %3 in shock.$} {bold green underscore}; +#SUB {^The%1grabs your%2. You struggle briefly but {she|he|it} wrests it from your grip and makes for a hasty retreat.} {<488><119>The<088>%1<488><119>grabs your<088>%2<488><119>. You struggle briefly but {she|he|it} wrests it from your grip and makes for a hasty retreat.}; +#SUB {^The%1and%2struggle briefly over%3before%4wrests it free%5$} {<488><119>The<088>%1<488><119>and<088>%2<488><119>struggle briefly over<088>%3<488><119>before%4wrests it free%5}; +#SUB {^The%1{suddenly snatches|snatches}%2from%3and makes a run for it.$} {<488><119>The<088><188>%1<488><119>suddenly snatches<088><188>%2<488><119>from<088><188>%3<488><119>and makes a run for it.}; +#HIGHLIGHT {^As %1 dies %2 drops %3 in shock.$} {green underscore}; #HIGHLIGHT {^When you open the %1 you think you can hear a faint rumbling sound from it.$} {red underscore}; #HIGHLIGHT {^Whoops! You tried to carry too many things and fumbled %1} {red underscore}; #HIGHLIGHT {%1 juggles around his stuff and fumbles %2} {red underscore}; +#HIGHLIGHT {^The sparkles around you fall to the ground and vanish.$} {red underscore}; +#HIGHLIGHT {^You sail past %1, flying over the side of the mountain in the process!$} {Red underscore}; +#ACTION {^You sail past %1, flying over the side of the mountain in the process!$} { + #showme {\a}; +}; #NOP ==[Command Color Highlighting]== #SUB {%w %w with a total of %* item} {<148>%1 %2<099> with a total of <148>%3<099> item}; #NOP ==[Truffle Found]== -#HIGHLIGHT {The young truffle pig unearths a truffle from the ground.} {bold green}; +#HIGHLIGHT {truffle pigs? unearths a truffle from the ground.} {Green}; #NOP ==[Attention Soul(s)]== #ACTION {^{(?!Ryattenoki).+} tugs on your sleeve in a desperate attempt to get some attention.$} { @@ -35,52 +45,76 @@ #NOP ==[Copperhead Mines]== #SUB {you think that %1 of the surrounding crates contain} {you think that <138>%1<099> of the surrounding crates contain}; -#HIGHLIGHT {Something doesn't smell right. You take a sniff but the faint odour of rotten eggs makes you cough.} {bold green underscore}; -#HIGHLIGHT {Somewhere above you, you hear the rumble of falling rock.} {bold green underscore}; -#ACTION {Something doesn't smell right. You take a sniff but the faint odour of rotten eggs makes you cough.} { - #showme {\a}; -}; -#ACTION {Somewhere above you, you hear the rumble of falling rock.} { - #showme {\a}; -}; +#SUB {a hammer chip would break %1 of rock} {a hammer chip would break <138>%1<099> of rock}; +#SUB {pickaxe strike would break %1 inches.} {pickaxe strike would break <138>%1<099> inches.}; +#SUB {You determine that directly behind this wall is a small gem vein of %1, exactly %2 deep.} {You determine that directly behind this wall is a small gem vein of %1, exactly <138>%2<099> deep.}; +#HIGHLIGHT {You shake your pan gently, as glittering purple gathers under the cloudy water.} {Magenta}; +#HIGHLIGHT {You shake your pan from side to side, as the last of the sediment leaves the pan and the water runs clear for just a moment.} {Magenta}; +#HIGHLIGHT {Something doesn't smell right. You take a sniff but the faint odour of rotten eggs makes you cough.} {Green underscore}; +#HIGHLIGHT {Somewhere above you, you hear the rumble of falling rock.} {Green underscore}; +#HIGHLIGHT {You suddenly feel like you can't breathe.} {Green underscore}; +#HIGHLIGHT {You collapse on the ground, your muscles spasming uncontrollably.} {Green underscore}; +#HIGHLIGHT {You can't smell it any more, but you're getting a dizzy sort of headache. Maybe you should get out of here.} {Green underscore}; +#HIGHLIGHT {The air here is just as choking as before.} {Green underscore}; +#ACTION {You shake your pan gently, as glittering purple gathers under the cloudy water.} { #showme {\a}; }; +#ACTION {You shake your pan from side to side, as the last of the sediment leaves the pan and the water runs clear for just a moment.} { #showme {\a}; }; +#ACTION {Something doesn't smell right. You take a sniff but the faint odour of rotten eggs makes you cough.} { #showme {\a}; }; +#ACTION {Somewhere above you, you hear the rumble of falling rock.} { #showme {\a}; }; +#ACTION {You suddenly feel like you can't breathe.} { #showme {\a}; }; +#ACTION {You collapse on the ground, your muscles spasming uncontrollably.} { #showme {\a}; }; +#ACTION {You can't smell it any more, but you're getting a dizzy sort of headache. Maybe you should get out of here.} { #showme {\a}; }; +#ACTION {The air here is just as choking as before.} { #showme {\a}; }; +#HIGHLIGHT {The air here seems fresher.} {Yellow underscore}; #NOP ==[Gemcutting/Finesmithing]== -#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and delicately chip %1} {bold green}; -#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and chip away %1} {bold red}; -#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and, struggling with %1} {bold red}; +#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and delicately chip %1} {green}; +#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and chip away %1} {Red}; +#HIGHLIGHT {You take the diamond-tipped chisel from your roll of gemcutting tools and, struggling with %1} {Red}; +#HIGHLIGHT {With a start, you realise that you've been going about this all wrong, %1} {Red}; #SUB {You estimate that the %1 is worth A$%2. You} {You estimate that the <149>%1<099> is worth A$<139>%2<099>. You}; #NOP ==[Achievements]== -#ACTION {^It is your turn to fight... enter the combat ring.$} { +#HIGHLIGHT {the {green|aqua|cyan|blue|yellow|red|orange|beige|brown|grey|black|violet|mauve|pink|purple} ball} {Cyan}; +#ACTION {^The %1 duellist has yielded to you, if this satisfies you then enter "accept surrender from duellist" to accept} { #showme {\a}; -} -#ACTION {^The %1 camel gallops away abruptly.$} { - #showme {[Pushover] %0}; -} +}; +#HIGHLIGHT {blackbird} {violet}; +#HIGHLIGHT {old {men|man|women|woman|shopper|shoppers|beggar|beggars}} {magenta}; +#ACTION {^One of the truffle pigs unearths a truffle from the ground.$} { + #delay {120} { #showme {CAN TRUFFLE FARM AGAIN<999>} } +}; + #NOP ==[PK Actions]== -#HIGHLIGHT {You reach towards your goal but apuse suddenly as %1 seems to sense something. {She|He|It} doesn't spot you and %2} {bold green}; -#HIGHLIGHT {You try to filch %1 from %2 but {she|he|it} notices you and manages to %3} {bold red}; +#HIGHLIGHT {You reach towards your goal but pause suddenly as %1 seems to sense something. {She|He|It} doesn't spot you and %2} {Green}; +#HIGHLIGHT {You try to filch %1 from %2 but {she|he|it} notices you and manages to %3} {Red}; #ACTION {A stream of expletives drifts down from above with %1} { #showme {\a}; }; #ACTION {There is the faintest of sounds, as of a gnat yawning, then a WHOOSH sound. %2} { #showme {\a}; }; -#HIGHLIGHT {A stream of expletives drifts down from above with %1} {bold red underscore}; -#HIGHLIGHT {There is the faintest of sounds, as of a gnat yawning, then a WHOOSH sound. %2} {bold red underscore}; +#HIGHLIGHT {A stream of expletives drifts down from above with %1} {Orange}; +#HIGHLIGHT {There is the faintest of sounds, as of a gnat yawning, then a WHOOSH sound. %2} {Lime}; #NOP ==[Shelox]== -#HIGHLIGHT {^Screaming echoes throughout the lair!$} {bold red}; -#HIGHLIGHT {^You see a thick string of web hurtling at you and then everything suddenly goes black.$} {bold green}; -#HIGHLIGHT {^Webbing shoots out from the frog that looks an awful lot like Queen Shelox's abdomen and slams into %1.$} {bold green}; -#HIGHLIGHT {^Webbing shoots out from Queen Shelox's abdomen and slams into %1.$} {bold green}; +#HIGHLIGHT {^Screaming echoes throughout the lair!$} {Red}; +#HIGHLIGHT {^You see a thick string of web hurtling at you and then everything suddenly goes black.$} {green}; +#SUB {^Webbing shoots out from the frog that looks an awful lot like Queen Shelox's abdomen and slams into %1.$} {<028>Webbing shoots out from the frog that looks an awful lot like Queen Shelox's abdomen and slams into <128>%1<028>.}; +#SUB {^Webbing shoots out from Queen Shelox's abdomen and slams into %1.$} {<028>Webbing shoots out from Queen Shelox's abdomen and slams into <128>%1<028>.}; #ACTION {^An ominous gurgling noise can be heard coming from the frog that looks an awful lot like Queen Shelox's abdomen.$} { #showme {\a}; }; #ACTION {^An ominous gurgling noise can be heard coming from Queen Shelox's abdomen.$} { #showme {\a}; }; -#HIGHLIGHT {^%1 giant spiders climb up from below!} {bold violet}; -#HIGHLIGHT {^%1 spiders scuttle up from below and join the fight!} {bold violet}; +#HIGHLIGHT {^%1 giant spiders climb up from below!} {Violet}; +#HIGHLIGHT {^%1 spiders scuttle up from below and join the fight!} {Violet}; + +#NOP ==[Tab Completions]== +#ACTION {^[%1 has gained the %2 achievement %3]$} { + #tab {%1}; + #return @log_line_to_chat{}; +} #NOP ==[End Session On Quit]== -#ACTION {Thanks for playing. See you next time.} { - #end; -} +#nop #ACTION {Thanks for playing. See you next time.} {; + #nop Don't do this anymore, since history buffer is with tt++ not tmux now, we want to see what we missed while idling out.; + #nop #end; +#nop }; #CLASS {actions} {close}; diff --git a/src/aliases.tin b/src/aliases.tin index 354574f..5bab210 100644 --- a/src/aliases.tin +++ b/src/aliases.tin @@ -1,5 +1,4 @@ -#ALIAS {setsize} {rows 300;cols 148}; -#ALIAS {setmsize} {rows 300;cols 60}; +#ALIAS {setsize} {rows 300;cols 999}; #ALIAS {^clear$} { #system {tput clear}; @@ -29,10 +28,10 @@ { #foreach {a;b;c;d;e;f} {cnt2} { - #var result <000>; + #local result <000>; #foreach {a;b;c;d;e;f} {cnt3} { - #var result $result <$cnt1$cnt2$cnt3> <<888>$cnt1$cnt2$cnt3><088> + #local result $result <$cnt1$cnt2$cnt3> <<888>$cnt1$cnt2$cnt3><088> }; #showme $result } @@ -40,49 +39,83 @@ } #ALIAS {minesolve %1 %2 %3} { - #var {total} {%1}; - #var {hammer} {%2}; - #var {pickaxe} {%3}; + #local {total} {%1}; + #local {hammer} {%2}; + #local {pickaxe} {%3}; - #var {solvecommand} {python3 -c 'import math; t,p,h = $total,$pickaxe,$hammer; pu,hu = int(math.floor(t/p)+1), int(math.floor(t/h)+1); print(*sorted([(i, j) for j in range(0,hu) for i in range(0,pu) if ((i*p) + (j*h) == t)], key=lambda t: t[1])[0]);'}; + #local {solvecommand} {python3 -c 'import math; t,p,h = $total,$pickaxe,$hammer; pu,hu = int(math.floor(t/p)+1), int(math.floor(t/h)+1); print(*sorted([(i, j) for j in range(0,hu) for i in range(0,pu) if ((i*p) + (j*h) == t)], key=lambda t: t[1])[0]);'}; #script {result} {$solvecommand}; #replace {result[1]} { } {;}; #list {solution} {create} {$result[1]}; - #format {minesolvedisplay} {<149>[MineSolver] Best solution is <139>%d<149> pickaxe strikes and <139>%d<149> hammer chips for a total of <159>%f<149> inches.<099>} {$solution[1]} {$solution[2]} {$total}; + #format {minesolvedisplay} {[mines] Best solution is <139>%d pickaxe strikes and <139>%d hammer chips for a total of <159>%f inches.<099>} {$solution[1]} {$solution[2]} {$total}; #echo {$minesolvedisplay}; }; #nop Resizing aliases for when we're in "cols 999" mode; #ALIAS {^skills%1} { - #send {cols 130}; + #send {cols 60}; #send {skills %1}; #send {cols 999}; }; -#ALIAS {^{speak|language}$} { - #send {cols 130}; +#ALIAS {^{speak|language|languages}$} { + #send {cols 60}; #send {speak}; #send {cols 999}; }; -#ALIAS {^spells$} { #send {cols 130}; #send {spells}; #send {cols 999}; }; -#ALIAS {^rituals$} { #send {cols 130}; #send {rituals}; #send {cols 999}; }; +#nop #ALIAS {^spells$} { #send {cols 60}; #nop #send {spells}; #nop #send {cols 999}; #nop }; +#ALIAS {^rituals$} { #send {cols 60}; #send {rituals}; #send {cols 999}; }; -#ALIAS {^group status$} { #send {cols 130}; #send {group status}; #send {cols 999}; }; +#ALIAS {^group status$} { #send {cols 60}; #send {group status}; #send {cols 999}; }; #ALIAS {^{who$|who (.+)$}} { - #send {cols 130}; + #send {cols 60}; #send {%0}; #send {cols 999}; }; #ALIAS {^help %1} { - #send {cols 130}; + #send {cols 60}; #send {help %1}; #send {cols 999}; }; #ALIAS {^nickname$} { - #send {cols 130}; + #send {cols 60}; #send {nickname}; #send {cols 999}; }; + +#ALIAS {^sotp} { + #send {stop}; +} + +#ALIAS {gatherstones} { + #10 get rock;put white rocks&white stones&white pebbles in knapsack;bury rocks in me&stones in me&pebbles in me +} + + +#nop Map Door Text custom match controls; +#ALIAS {mdtc add %1} { + #local {mdtc_command} {python3 src/mdt_matchcontrol.py add %1}; + #script {result} {$mdtc_command}; + + #if {"$result" == "0"} { + + #echo {[mdt] <138>* Failed to add a new custom match for '%1'.}; + #return; + } { + #echo {[mdt] <138>* Successfully added a new custom match for '%1'.}; + }; +}; +#ALIAS {mdtc remove %1} { + #local {mdtc_command} {python3 src/mdt_matchcontrol.py remove %1}; + #script {result} {$mdtc_command}; + + #if {"$result" == "0"} { + #echo {[mdt] <138>* Failed to remove a custom match for '%1'.}; + #return; + } { + #echo {[mdt] <138>* Successfully removed a custom match for '%1'.}; + }; +}; diff --git a/src/channels.tin b/src/channels.tin index 7dfab8d..2ca22c1 100644 --- a/src/channels.tin +++ b/src/channels.tin @@ -3,20 +3,22 @@ #VARIABLE {talker_log_toggle} {0}; -#FUNCTION {log_line_to_chat} { - #script {ts} {date +"%I:%M%P"}; - #line {log} {$chat_file} {$ts[1] \}; - #line {log} {$chat_file} -}; - #ACTION {^You {tell|ask|exclaim to} %1:%2} {#return @log_line_to_chat{};}; #ACTION {^%1 {tells|asks|exclaims to} %*you{|(?: in \w+)?}: %4} {#return @log_line_to_chat{};}; -#ACTION {^[%1] %2{:|} %3} {#return @log_line_to_chat{};}; + +#nop Capture group chat best we can, but ignore any of our client-side [x] messages at least; +#ACTION {^[{(?!(talker|spottimers|missions|speedwalk|sail|quota|mdt|group|eff|snatch)).+}] %2{: | }%4} { + #nop %1 is group name, %3 is player name, %6 is message; + #tab {%3}; + #return @log_line_to_chat{}; +}; #ACTION {^(They Might Be Alts) } {#return @log_line_to_chat{};}; +#ACTION {^(The Unsinkables) } {#return @log_line_to_chat{};}; +#ACTION {^(Faculty) } {#return @log_line_to_chat{};}; #ACTION {^(newbiehelpers)} {#return @log_line_to_chat{};}; -#ACTION {^You newbiehelper-tell:%1} {#return @log_line_to_chat{};}; +#ACTION {^You newbiehelpers-tell:%1} {#return @log_line_to_chat{};}; #ACTION {^(newbie)} {#return @log_line_to_chat{};}; #ACTION {^You newbie-tell:%1} {#return @log_line_to_chat{};}; #ACTION {^You special-tell:%1} {#return @log_line_to_chat{};}; @@ -28,9 +30,6 @@ #return @log_line_to_chat{}; }; }; -#ACTION {(>CTF<) %1} { - #return @log_line_to_chat{}; -}; #ACTION {%1Inform: %2} { #return @log_line_to_chat{}; }; @@ -44,6 +43,21 @@ #return @log_line_to_chat{}; }; }; +#ACTION {^(Priests) %1 wisps{:|} %2} { + #if {"$talker_log_toggle" == "1"} { + #return @log_line_to_chat{}; + }; +}; +#ACTION {^(Pishe) %1 wisps{:|} %2} { + #if {"$talker_log_toggle" == "1"} { + #return @log_line_to_chat{}; + }; +}; +#ACTION {^(Witches) %1 wisps{:|} %2} { + #if {"$talker_log_toggle" == "1"} { + #return @log_line_to_chat{}; + }; +}; #ACTION {^(Igame) %1 wisps{:|} %2} { #if {"$talker_log_toggle" == "1"} { #return @log_line_to_chat{}; @@ -52,11 +66,11 @@ #ALIAS {talker log %1} { #if {"%1" == "on"} { - #echo {<149>[Talker] Now logging talker chat to monitor.<099>}; + #echo {[talker] Now logging talker chat to monitor.<099>}; #var {talker_log_toggle} {1}; }; #if {"%1" == "off"} { - #echo {<149>[Talker] No longer logging talker chat to monitor.<099>}; + #echo {[talker] No longer logging talker chat to monitor.<099>}; #var {talker_log_toggle} {0}; }; }; diff --git a/src/combat.tin b/src/combat.tin index 8c442b4..a1b94e0 100644 --- a/src/combat.tin +++ b/src/combat.tin @@ -1,50 +1,131 @@ #CLASS {combat} {kill}; #CLASS {combat} {open}; -#VARIABLE {my_attacks_color} {orange}; -#VARIABLE {enemy_attack_color} {red}; +#nop == Colors ==; +#VARIABLE {color_special_prep} {}; +#VARIABLE {color_special_fail} {}; -#NOP ==[My Specials]== -#HIGHLIGHT {^You attempt to prepare %1} {bold red}; -#HIGHLIGHT {^You prepare to {stab|slice|trip|shove|impale|kick|punch} %2} {bold green}; -#HIGHLIGHT {^You attempt to launch a powerful attack. %1} {bold red}; -#HIGHLIGHT {^You launch a powerful attack. %1} {bold green}; -#HIGHLIGHT {^You launch a powerful attack, landing a %1} {bold green}; -#HIGHLIGHT {^You try to {trip|shove} %1} {bold red}; -#HIGHLIGHT {^You shove %1} {bold green}; -#HIGHLIGHT {^You trip %1} {bold green}; -#HIGHLIGHT {^You lose the moment.} {bold red}; +#VARIABLE {color_melee_out_fail} {}; +#VARIABLE {color_melee_out_success} {}; +#VARIABLE {color_special_out_fail} {}; +#VARIABLE {color_special_out_success} {}; + +#VARIABLE {color_melee_inc_fail} {}; +#VARIABLE {color_melee_inc_success} {}; +#VARIABLE {color_special_inc_fail} {}; +#VARIABLE {color_special_inc_success} {}; + +#VARIABLE {color_melee_third_fail} {}; +#VARIABLE {color_melee_third_success} {}; +#VARIABLE {color_special_third_fail} {}; +#VARIABLE {color_special_third_success} {}; -#NOP ==[Backstabs]== -#HIGHLIGHT {^You {skilfully |silently |}sneak around %1 without being spotted and manage to %2.$} {bold green}; -#HIGHLIGHT {^You hold your cover as you {skillfully |silently |}sneak around %1 and manage to catch %w by surprise.$} {bold green}; -#HIGHLIGHT {^You %1 with %2 but despite the surprise %3.$} {bold red} {2}; -#HIGHLIGHT {^Moving {swiftly|quickly|too fast to see}, you %1 {with|right through} %2.$} {bold green}; -#HIGHLIGHT {^Catching %w {by surprise|off balance}, you %1 {with|right through} %2.$} {bold green}; -#HIGHLIGHT {^With {a blur of speed|surprising speed}, you %1 {with|right through} %2.$} {bold green}; -#HIGHLIGHT {^Quick as lightning, you %1 {with|right through} %2.$} {bold green}; -#HIGHLIGHT {Striking like a cobra, you %1 {with|right through} %2.$} {bold green}; +#nop Hash marks are 110 and 148 characters, typical widths for me +#HIGHLIGHT {^12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890#234567890123456789012345678901234567#90}; + +#nop ==[My Specials]== +#nop special prep; +#HIGHLIGHT {^You prepare to {bash|beat|chop|feint|hack|kick|pierce|punch|riposte|shove|slash|smash|stab|trip|crush|behead|impale|flick|launch a wildswing attack} %1} {$color_special_prep}; +#nop special fail; +#HIGHLIGHT {^You attempt to prepare a special attack but {(?:\w+\b\W*?){1,8}} feels too unwieldy and you lose the initiative.$} {$color_special_fail}; +#nop special out fail; +#HIGHLIGHT {^You attempt to launch a powerful attack. %1} {$color_special_out_fail}; +#HIGHLIGHT {^You attempt to {shove|trip} %2, but cannot find an opportunity} {$color_special_out_fail}; +#HIGHLIGHT {^You try to {trip |shove }{(?:\w+\b\W*?){1,8}} but fail %3} {$color_special_out_fail}; +#HIGHLIGHT {^You lose the moment\.$} {$color_special_out_fail}; +#HIGHLIGHT {^You move as though about to launch a powerful attack on {(?:\w+\b\W*?){1,8}}\. {?>He|She|It} sees through your feint and doesn't react\.$} {$color_special_out_fail}; +#nop special out success; +#HIGHLIGHT {^You launch a powerful attack{.|,} %1} {$color_special_out_success}; +#HIGHLIGHT {^You {shove|trip} {(?!the corpse of).+}} {$color_special_out_success}; +#HIGHLIGHT {^You move as though about to launch a powerful attack on %1\. {She|He|It} falls for your feint, leaving %3} {$color_special_out_success}; -#NOP ==[Me Attacking Them]== -#HIGHLIGHT {^You {viciously |messily |barely |neatly |}{thrust|snick|fillet|poke|chop up|chop|jab|scratch|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|take} {a sliver off |into |}%1{'?s?} %2 {on|with} %3} {$my_attacks_color}; -#HIGHLIGHT {^You {viciously |messily |barely |neatly |}stab %1 {deeply |}in %2 with %3} {$my_attacks_color}; -#HIGHLIGHT {^You stab %1 right through %2's %3} {$my_attacks_color}; -#HIGHLIGHT {^You {viciously |messily |barely |neatly |}{poke|kick|punch|jab|boot|tickle|kick out} %1 in %2} {$my_attacks_color}; -#HIGHLIGHT {^You {punch|kick out} at %1 but {his|her} %2 absorbs {some|most} of the blow\.$} {$my_attacks_color}; +#nop ==[Backstabs]== +#HIGHLIGHT {^You %1 with %2 but despite the surprise %3.$} {$color_special_out_fail} {2}; +#HIGHLIGHT {^You {skilfully |silently |}sneak around %1 without being spotted and manage to %2.$} {$color_special_out_success}; +#HIGHLIGHT {^You hold your cover as you {skillfully |silently |}sneak around %1 and manage to catch %w by surprise.$} {$color_special_out_success}; +#HIGHLIGHT {^Moving {swiftly|quickly|too fast to see}, you %1 {with|right through} %2.$} {$color_special_out_success}; +#HIGHLIGHT {^Catching %w {by surprise|off balance}, you %1 {with|right through} %2.$} {$color_special_out_success}; +#HIGHLIGHT {^With {a blur of speed|surprising speed}, you %1 {with|right through} %2.$} {$color_special_out_success}; +#HIGHLIGHT {^Quick as lightning, you %1 {with|right through} %2.$} {$color_special_out_success}; +#HIGHLIGHT {Striking like a cobra, you %1 {with|right through} %2.$} {$color_special_out_success}; -#NOP == [Them Attacking Me]== -#HIGHLIGHT {%1 but your %2 absorbs {some|most} of the blow.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 runs you through the %2 with %3.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s %w %3 into your %4.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s your %2 {into pieces |}{with|on} %w %4.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s into your %2 with %w %3.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s you {deeply |}in the %2.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s you {deeply |}in the %2 with %3.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 pierces your %2 with %3.$} {$enemy_attack_color}; -#HIGHLIGHT {%1 hitting your %2.$} {$enemy_attack_color}; +#nop ==[Me Attacking Them]== +#HIGHLIGHT {^You {thrust|slash|swing} at %1 with {one of }your %2 but{,?} %3} {$color_melee_out_fail}; +#HIGHLIGHT {^You {tap |bruise |smash |viciously |messily |barely |neatly |}{snick|fillet|poke|chop up|chop|jab|scratch|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|take} {a sliver off |into |}%1{'?s?} %2 {on|with} %3} {$color_melee_out_success}; +#HIGHLIGHT {^You slice your %1 {deeply |}into %2's %3.$}; +#HIGHLIGHT {^You {viciously |messily |barely |neatly |}stab %1 {deeply |}in %2 with %3} {$color_melee_out_success}; +#HIGHLIGHT {^You beat %1 to a pulp\.$} {$color_melee_out_success}; +#HIGHLIGHT {^You stab %1 right through %2's %3} {$color_melee_out_success}; +#HIGHLIGHT {^You {viciously |messily |barely |neatly |}{poke|kick|punch|jab|boot|tickle|kick out} %1 in %2} {$color_melee_out_success}; +#HIGHLIGHT {^You {punch|kick out} at %1 but {his|her} %2 absorbs {some|most} of the blow\.$} {$color_melee_out_success}; + + +#nop ==[Their Specials At Me]== +#nop specials incoming failures; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} reaches for you and stumbles.$} {$color_special_inc_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} {trips|shoves} you, causing you to stumble slightly before regaining your balance.$} {$color_special_inc_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} moves as though about to launch a powerful attack but you realise it is a feint and do not react\.$} {$color_special_inc_fail}; +#HIGHLIGHT {^You suddenly {?>sense a presence|hear a noise} behind you and turn around to {?>see|find} {(?:\w+\b\W*?){1,8}} preparing to backstab you\. %4} {$color_special_inc_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} attempts to launch a powerful attack. %2 at you {(with (his|her|its) .+)?}but %3} {$color_special_inc_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} attempts to launch a powerful attack, landing %2 but {you|your} %3} {$color_special_inc_fail}; +#HIGHLIGHT {^{(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {.*} you {.*} but you{r?} {(?!\w+ skin).+}} {$color_special_inc_fail}; +#nop specials incoming success; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} moves as though about to launch a powerful attack and you prepare to defend yourself\. Too{.*}} {$color_special_inc_success}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} attacks you from behind\. You recall {?>him|her|it} standing right in front of you just a second ago\.$} {$color_special_inc_success}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {(?!.*but).*} you{r?} {(?!.*but).*}} {$color_special_inc_success}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {.*} you {.*} but your {.*} skin{.*}} {$color_special_inc_success}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} {trips|shoves} you, {causing you to stumble|sending you crashing to the ground}.$} {$color_special_inc_success}; + + +#nop ==[Them Attacking Me]== +#nop melee incoming fail +#HIGHLIGHT {^{?:(?!(You|.* powerful) )(.*)} {at|to gore|on|bite} you {(with (one of )?(?:his|her|its) .* )?}but{( despite the surprise)?, although unable to defend, | }you{r?} {((?!.* skin ).*)}} {$color_melee_inc_fail}; +#nop melee incoming success +#HIGHLIGHT {^{(?!(You|.* powerful) )(\w+\b\W*?){1,8}} {visciously |messily |barely |neatly |}{crushe|strike|batter|smashe|bruise|ventilate|poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|punche|spike}s you {(deeply )?}in the {.*} with {(one of )?}{his|her|its} {.*} but your {.*} skin{.*}} {$color_melee_inc_success}; +#HIGHLIGHT {^{(?!(You|.* powerful) )(\w+\b\W*?){1,8}} {visciously |messily |barely |neatly |}{crushe|strike|batter|smashe|bruise|ventilate|poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|punche|spike}s you {deeply |}in the {.*} with {(one of )?}{his|her|its} {(?!.* but).*}} {$color_melee_inc_success}; + +#nop #HIGHLIGHT {{?:(?!powerful )(?:\w\+\b\W*?){1,8}} {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s %w %3 into your %4.$} {$color_melee_inc_success}; +#nop #HIGHLIGHT {{?:(?!powerful )(?:\w\+\b\W*?){1,8}} {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s your %2 {into pieces |}{with|on} %w %4.$} {$color_melee_inc_success}; +#nop #HIGHLIGHT {{?:(?!powerful )(?:\w\+\b\W*?){1,8}} {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s into your %2 with %w %3.$} {$color_melee_inc_success}; +#nop #HIGHLIGHT {{?:(?!powerful )(?:\w\+\b\W*?){1,8}} {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s you {deeply |}in the %2.$} {$color_melee_inc_success}; +#nop #HIGHLIGHT {{?:(?!powerful )(?:\w\+\b\W*?){1,8}} {viciously |messily |barely |neatly |}{poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer}s you {deeply |}in the %2 with %3.$} {$color_melee_inc_success}; +#HIGHLIGHT {%1 runs you through the %2 with %3.$} {$color_melee_inc_success}; + + +#nop ==[Specials Not Directed At Me]== +#nop specials third fail +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} moves towards {?:(?!you )(?:\w+\b\W*?){1,8}} as though about to launch a powerful attack\. {?:(?!You )(?:\w+\b\W*?){1,8}} does not react, %4} {$color_special_third_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} reaches for {?:(?!you )(?:\w+\b\W*?){1,8}} and stumbles\.$} {$color_special_third_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} attempts to launch a powerful attack. %2 at {?:(?!you )(.*)}} {$color_special_third_fail}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {?:(?!.*(Y|y)ou )(.*)} but {his|her|its|he|she|it} {(?!.*skin).*}} {$color_special_third_fail}; +#nop specials third success +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {(?!.*((Y|y)ou|but) )(.*)}} {$color_special_third_success}; +#HIGHLIGHT {^{?:(?!You )(?:\w+\b\W*?){1,8}} launches a powerful attack{\. |,} {?:(?!.*(Y|y)ou )(.*)} but {his|her|its|he|she|it} {.*} skin{.*}} {$color_special_third_success}; + + +#nop ==[Attacks Not Directed At Me]== +#nop melee third fail +#HIGHLIGHT {^{?:(?!(You |.+powerful))(?:\w+\b\W*?){1,8}}{(?!.+powerful)(.+)} {crushe|strike|batter|smashe|bruise|ventilate|poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|punche|spike|swing}s {((his|her|its).+)?}at {?:(?!(You |.+powerful))(?:\w+\b\W*?){1,8}} but {(?!.+ skin)}{.*}} {$color_melee_third_fail}; +#HIGHLIGHT {^{?:(?!(You |.+powerful))(?:\w+\b\W*?){1,8}}{(?!.+powerful)(.+)} {thrust|swing|slashe}s {(his|her|its).*}?at {?:(?!You )(.*)} but{,?} {(?!.+ skin)}{.+}} {$color_melee_third_fail}; +#nop melee third success +#HIGHLIGHT {^{(?!.+powerful).+} {crushe|strike|batter|smashe|bruise|ventilate|poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|punche|spike}s {his|her|its} %4 {across|into} %6} {$color_melee_third_success}; +#HIGHLIGHT {^{(?!.+powerful).+} {crushe|strike|batter|smashe|bruise|ventilate|poke|chop|jab|scratche|boot|kick|hit|tickle|hack|prick|shred|nick|cut|stab|perforate|pierce|impale|slice|skewer|punche|spike}s %3 {with|on} %5} {$color_melee_third_success}; +#HIGHLIGHT {{?:(?!You )(?:\w+\b\W*?){1,8}} runs %2 through %3 with {his|her|its} %4} {$color_melee_third_success}; +#HIGHLIGHT {{?:(?!You )(?:\w+\b\W*?){1,8}} takes a sliver off %2 with {his|her|its} %3} {$color_melee_third_success}; + +#nop ===== DOCUMENTING ISSUES TO FIX LATER =====; +#nop A burnt torch is hanging from a stall and a pair of plain leather boots is on the ground.; +#nop A green wool shirt, a pair of hard leather boots and a pair of heavy cotton breeches are on the ground.; +#nop A pair of heavy socks, a pair of hard leather boots and a pair of heavy cotton breeches are on the ground.; +#nop A pair of plain leather boots is on the ground.; +#nop A street lamp is here and a pair of light grey kidskin ladies' boots is on the ground.; +#nop The Grand Wizard clock strikes the half-hour with a deep, rolling chime that sounds like a death knell.; +#nop ===========================================; +#nop == BELOW IS UNHIGHLIGHTED BUT SHOULD BE ==; +#nop You attempt to punch Arlof Eetmmot but you move too slowly and lose the initiative.; #CLASS {combat} {close}; diff --git a/src/db.tin b/src/db.tin index bbcab6c..2c2b7ba 100644 --- a/src/db.tin +++ b/src/db.tin @@ -1,6 +1,8 @@ #CLASS {database} {kill}; #CLASS {database} {open}; + +#VARIABLE {dbbookmark_file} {logs/features/dbbookmarksync.tin}; #VARIABLE {map_id_names} { {1}{Ankh-Morpork} {2}{AM Assassins} @@ -97,7 +99,7 @@ #list {last_query_list} {clear}; #if {$gatherable_result_size > 0} { - #format {gatherable_display} {%cResults for gatherables matching "%c%s%c":} {bold blue}{yellow}{%1}{bold blue}; + #format {gatherable_display} {%cResults for gatherables matching "%c%s%c":<099>} {Azure}{Yellow}{%1}{Azure}; #echo {$gatherable_display}; #var {gather_count} {0}; @@ -113,17 +115,18 @@ {map_name}{$map_id_names[$gatherable_results[4]]} }; - #format {gatherable_display} {%c [%c%d%c]: %c%s%c found in %c%s%c,%c %s%c} {bold blue}{cyan}{$gather_count}{bold blue}{green}{$gatherable_info[item_name]}{bold blue}{orange}{$gatherable_info[room_short]}{bold blue}{orange}{$gatherable_info[map_name]}{bold blue}; - #echo {$gatherable_display}; + #showme { [@dbroute_link{$gather_count;$gather_count}]: @dbroute_link{$gather_count;$gatherable_info[item_name]} found in @dbroute_link{$gather_count;$gatherable_info[room_short]}, $gatherable_info[map_name]<099>}; #variable {last_query_list[$gather_count]} {$gatherable_info}; #math {gather_count} {$gather_count + 1}; - } + }; + #showme {Generate a walking path with "db route #" or *clicking* the number or name of one of the results above.}; + #showme {}; } { - #format {gatherable_display} {%cQuery Result: %c%s%c was not found.} {bold blue}{green}{%1}{bold blue}; + #format {gatherable_display} {%cQuery Result: %c%s%c was not found.<099>} {Azure}{Green}{%1}{Azure}; #echo {$gatherable_display}; - } -} + }; +}; #ALIAS {db npc {[^{}\n]+}{( \{([\w\s-]+)\})?}$} { #unvariable {npc_display} {search_term} {query}; @@ -134,12 +137,12 @@ #nop If we found an area name in curly braces, try to match a map-set; #var {search_map} {@map_parse{%4}}; - #if {&map_term} { + #if {&{map_term}} { #format {query} {SELECT npc_name, rooms.map_id, rooms.room_id, rooms.room_short, INSTR(npc_name, '%s') pos FROM npc_info INNER JOIN rooms on rooms.room_id = npc_info.room_id WHERE pos > 0 AND rooms.map_id IN (%s) LIMIT 10} {$search_term} {$map_term}; }; #else { #format {query} {SELECT npc_name, rooms.map_id, rooms.room_id, rooms.room_short, INSTR(npc_name, '%s') pos FROM npc_info INNER JOIN rooms on rooms.room_id = npc_info.room_id WHERE pos > 0 LIMIT 10} {$search_term}; - } + }; #format {db_command} {sqlite3 -separator ";" src/quow.db "%s"} {$query}; #script {npc_query_result} {$db_command}; @@ -148,7 +151,7 @@ #list {last_query_list} {clear}; #if {$npc_result_size > 0} { - #format {npc_display} {%cResults for NPCs matching "%c%s%c":} {bold blue}{yellow}{%1}{bold blue}; + #format {npc_display} {%cResults for NPCs matching "%c%s%c":<099>} {Azure}{Yellow}{%1}{Azure}; #echo {$npc_display}; #var {npc_count} {0}; @@ -164,14 +167,15 @@ {room_short}{$npc_results[4]} }; - #format {npc_display} {%c [%c%d%c]: %c%s%c found in %c%s%c,%c %s%c} {bold blue}{cyan}{$npc_count}{bold blue}{green}{$npc_info[npc_name]}{bold blue}{orange}{$npc_info[room_short]}{bold blue}{orange}{$npc_info[map_name]}{bold blue}; - #echo {$npc_display}; + #showme { [@dbroute_link{$npc_count;$npc_count}]: @dbroute_link{$npc_count;$npc_info[npc_name]} found in @dbroute_link{$npc_count;$npc_info[room_short]}, $npc_info[map_name]<099>}; #variable {last_query_list[$npc_count]} {$npc_info}; #math {npc_count} {$npc_count + 1}; - } + }; + #showme {Generate a walking path with "db route #" or *clicking* the number or name of one of the results above.}; + #showme {}; } { - #format {npc_display} {%cQuery Result: %c%s%c was not found.} {bold blue}{green}{%1}{bold blue}; + #format {npc_display} {%cQuery Result: %c%s%c was not found.<099>} {Azure}{Green}{%1}{Azure}; #echo {$npc_display}; } }; @@ -185,12 +189,12 @@ #nop If we found an area name in curly braces, try to match a map-set; #var {search_map} {@map_parse{%4}}; - #if {&map_term} { + #if {&{map_term}} { #format {query} {SELECT npc_info.map_id, npc_name, npc_info.room_id, item_name, room_short, INSTR(item_name, '%s') pos FROM 'npc_info' INNER JOIN npc_items ON npc_items.npc_id = npc_info.npc_id INNER JOIN rooms on npc_info.room_id = rooms.room_id WHERE pos > 0 AND rooms.map_id IN (%s) LIMIT 10} {$search_term} {$map_term}; }; #else { #format {query} {SELECT npc_info.map_id, npc_name, npc_info.room_id, item_name, room_short, INSTR(item_name, '%s') pos FROM 'npc_info' INNER JOIN npc_items ON npc_items.npc_id = npc_info.npc_id INNER JOIN rooms on npc_info.room_id = rooms.room_id WHERE pos > 0 LIMIT 10} {$search_term}; - } + }; #format {db_command} {sqlite3 -separator ";" src/quow.db "%s"} {$query}; #script {npcitem_query_result} {$db_command}; @@ -199,7 +203,7 @@ #list {last_query_list} {clear}; #if {$npcitem_result_size > 0} { - #format {npcitem_display} {%cResults for NPC items matching "%c%s%c":} {bold blue}{yellow}{%1}{bold blue}; + #format {npcitem_display} {%cResults for NPC items matching "%c%s%c":<099>} {Azure}{Yellow}{%1}{Azure}; #echo {$npcitem_display}; #var {npcitem_count} {0}; @@ -216,14 +220,15 @@ {room_short}{$npcitem_results[5]} }; - #format {npcitem_display} {%c [%c%d%c]: %c%s%c found on %c%s%c in %c%s%c,%c %s%c} {bold blue}{cyan}{$npcitem_count}{bold blue}{green}{$npcitem_info[item_name]}{bold blue}{white}{$npcitem_info[npc_name]}{bold blue}{orange}{$npcitem_info[room_short]}{bold blue}{orange}{$npcitem_info[map_name]}{bold blue}; - #echo {$npcitem_display}; + #showme { [@dbroute_link{$npcitem_count;$npcitem_count}]: @dbroute_link{$npcitem_count;$npcitem_info[item_name]} found on $npcitem_info[npc_name] in @dbroute_link{$npcitem_count;$npcitem_info[room_short]}, $npcitem_info[map_name]<099>}; #variable {last_query_list[$npcitem_count]} {$npcitem_info}; #math {npcitem_count} {$npcitem_count + 1}; - } + }; + #showme {Generate a walking path with "db route #" or *clicking* the number or name of one of the results above.}; + #showme {}; } { - #format {npcitem_display} {%cQuery Result: %c%s%c was not found.} {bold blue}{green}{%1}{bold blue}; + #format {npcitem_display} {%cQuery Result: %c%s%c was not found.<099>} {Azure}{Green}{%1}{Azure}; #echo {$npcitem_display}; } }; @@ -237,12 +242,12 @@ #nop If we found an area name in curly braces, try to match a map-set; #var {search_map} {@map_parse{%4}}; - #if {&map_term} { + #if {&{map_term}} { #format {query} {SELECT item_name, sale_price, room_short, shop_items.room_id, map_id, INSTR(item_name, '%s') pos FROM shop_items INNER JOIN rooms on rooms.room_id = shop_items.room_id WHERE pos > 0 AND sale_price != 'gather' AND map_id IN (%s) LIMIT 10;} {$search_term} {$map_term}; }; #else { #format {query} {SELECT item_name, sale_price, room_short, shop_items.room_id, map_id, INSTR(item_name, '%s') pos FROM shop_items INNER JOIN rooms on rooms.room_id = shop_items.room_id WHERE pos > 0 AND sale_price != 'gather' LIMIT 10;} {$search_term}; - } + }; #format {db_command} {sqlite3 -separator ";" src/quow.db "%s"} {$query}; #script {item_query_result} {$db_command}; @@ -251,7 +256,7 @@ #list {last_query_list} {clear}; #if {$item_result_size > 0} { - #format {item_display} {%cResults for items matching "%c%s%c":} {bold blue}{yellow}{%1}{bold blue}; + #format {item_display} {%cResults for items matching "%c%s%c":<099>} {Azure}{Yellow}{%1}{Azure}; #echo {$item_display}; #var {item_count} {0}; @@ -268,14 +273,17 @@ {map_name}{$map_id_names[$item_results[5]]} }; - #format {item_display} {%c [%c%d%c]: %c%s%c (%c%s%c) found in %c%s%c,%c %s%c} {bold blue}{cyan}{$item_count}{bold blue}{green}{$item_info[item_name]}{bold blue}{yellow}{$item_info[sale_price]}{bold blue}{orange}{$item_info[room_short]}{bold blue}{orange}{$item_info[map_name]}{bold blue}; - #echo {$item_display}; + #nop #format {item_display} {%c [%c%d%c]: %c%s%c (%c%s%c) found in %c%s%c,%c %s%c<099>} {Azure}{Cyan}{$item_count}{Azure}{Green}{$item_info[item_name]}{Azure}{Yellow}{$item_info[sale_price]}{Azure}{Orange}{$item_info[room_short]}{Azure}{Orange}{$item_info[map_name]}{Azure}; + #nop #echo {$item_display}; + #showme { [@dbroute_link{$item_count;$item_count}]: @dbroute_link{$item_count;$item_info[item_name]} ($item_info[sale_price]) found in @dbroute_link{$item_count;$item_info[room_short]}, $item_info[map_name]<099>}; #variable {last_query_list[$item_count]} {$item_info}; #math {item_count} {$item_count + 1}; }; + #showme {Generate a walking path with "db route #" or *clicking* the number or name of one of the results above.}; + #showme {}; } { - #format {item_display} {%cQuery Result: %c%s%c was not found.} {bold blue}{green}{%1}{bold blue}; + #format {item_display} {%cQuery Result: %c%s%c was not found.<099>} {Azure}{Green}{%1}{Azure}; #echo {$item_display}; }; }; @@ -289,12 +297,12 @@ #nop If we found an area name in curly braces, try to match a map-set; #var {search_map} {@map_parse{%4}}; - #if {&map_term} { + #if {&{map_term}} { #format {query} {SELECT room_id, map_id, room_short, room_type, INSTR(room_short, '%s') pos FROM rooms WHERE pos > 0 AND map_id IN (%s) LIMIT 10;} {$search_term} {$map_term}; }; #else { #format {query} {SELECT room_id, map_id, room_short, room_type, INSTR(room_short, '%s') pos FROM rooms WHERE pos > 0 LIMIT 10;} {$search_term}; - } + }; #format {db_command} {sqlite3 -separator ";" src/quow.db "%s"} {$query}; #script {room_query_result} {$db_command}; @@ -303,7 +311,7 @@ #list {last_query_list} {clear}; #if {$room_result_size > 0} { - #format {room_display} {%cResults for items items matching "%c%s%c":} {bold blue}{yellow}{%1}{bold blue}; + #format {room_display} {%cResults for items items matching "%c%s%c":<099>} {Azure}{Yellow}{%1}{Azure}; #echo {$room_display}; #var {room_count} {0}; @@ -319,14 +327,16 @@ {room_type}{$room_results[4]} }; - #format {room_display} {%c [%c%d%c]: %c%s%c (%c%s%c) found in %c%s%c} {bold blue}{cyan}{$room_count}{bold blue}{green}{$room_info[room_short]}{bold blue}{yellow}{$room_info[room_type]}{bold blue}{orange}{$room_info[map_name]}{bold blue}; - #echo {$room_display}; + #nop #format {room_display} {%c [%c%d%c]: %c%s%c (%c%s%c) found in %c%s%c<099>} {Azure}{Cyan}{$room_count}{Azure}{Green}{$room_info[room_short]}{Azure}{Yellow}{$room_info[room_type]}{Azure}{Orange}{$room_info[map_name]}{Azure}; + #showme { [@dbroute_link{$room_count;$room_count}]: @dbroute_link{$room_count;$room_info[room_short]} ($room_info[room_type]) found in $room_info[map_name]<099>}; #variable {last_query_list[$room_count]} {$room_info}; #math {room_count} {$room_count + 1}; - } + }; + #showme {Generate a walking path with "db route #" or *clicking* the number or name of one of the results above.}; + #showme {}; } { - #format {room_display} {%cQuery Result: %c%s%c was not found.} {bold blue}{green}{%1}{bold blue}; + #format {room_display} {%cQuery Result: %c%s%c was not found.<099>} {Azure}{Green}{%1}{Azure}; #echo {$room_display}; }; }; @@ -334,21 +344,20 @@ #ALIAS {db route %1} { #if {&last_query_list[%1]} { #var {target_room} {$last_query_list[%1]}; - #format {route_display} {%c[speedwalk] Generating speedwalk from current location to %c%s%c, %c%s%c...} {bold blue}{green}{$target_room[room_short]}{bold blue}{orange}{$target_room[map_name]}{bold blue}; - + #format {route_display} {[speedwalk] Generating speedwalk from current location to %c%s, %c%s...<099>} {Green}{$target_room[room_short]}{Orange}{$target_room[map_name]}; #echo {$route_display}; #var {route_current_room_id} {$GMCP[room][info][identifier]}; #var {route_target_room_id} {$target_room[room_id]}; - #format {route_command} {python src/maproute.py %s %s} {$route_current_room_id} {$route_target_room_id}; + #format {route_command} {python3 src/maproute.py %s %s} {$route_current_room_id} {$route_target_room_id}; #script {speedwalk_result} {$route_command}; #if {"$speedwalk_result[1]" == "0"} { - #format {route_error} {%c[speedwalk] Unable to find route to %c%s%c or unknown current room.} {bold blue}{green}{$target_room[room_short]}{bold blue}; + #format {route_error} {[speedwalk] Unable to find route to %c%s or unknown current room.<099>} {Green}{$target_room[room_short]}; #echo {$route_error}; #return; - } + }; #send {$speedwalk_result[1]}; #variable {route_length} {$speedwalk_result[1]}; @@ -357,16 +366,220 @@ #list {route_length} {create} {$route_length}; #list {route_length} {size} {route_length}; - #format {route_confirm} {%c[speedwalk] A route was found, type "%cspeedwalk%c" to fast travel. [%g steps]} {bold blue}{green}{bold blue}{$route_length}; + #format {route_confirm} {[speedwalk] A route was found, type "%c@dbspeedwalk_link{}" to fast travel. [%c%g steps]<099>} {Green}{Cyan}{$route_length}; #echo {$route_confirm}; } { - #format {route_display} {%c[speedwalk] No previous query data found, try searching first.} {bold blue}; + #format {route_display} {[speedwalk] No previous query data found, try searching first.<099>}; #echo {$route_display}; - } + }; }; #ALIAS {speedwalk} { #send {RuhsSpeedRun}; -} +}; + +#ALIAS {db routeto %1} { + #var {target_room} {%1}; + #format {route_display} {[speedwalk] Generating speedwalk from current location to specified room. %s<099>} {%1}; + + #echo {$route_display}; + + #var {route_current_room_id} {$GMCP[room][info][identifier]}; + #var {route_target_room_id} {%1}; + + #format {route_command} {python3 src/maproute.py %s %s} {$route_current_room_id} {$route_target_room_id}; + #script {speedwalk_result} {$route_command}; + + #if {"$speedwalk_result[1]" == "0"} { + #format {route_error} {[speedwalk] Unable to find route to %c%s or unknown current room.<099>} {Green}{$target_room[room_short]}; + #echo {$route_error}; + #return; + }; + #send {$speedwalk_result[1]}; + + #variable {route_length} {$speedwalk_result[1]}; + #replace {route_length} {\\\;} {;}; + #replace {route_length} { } {}; + #list {route_length} {create} {$route_length}; + #list {route_length} {size} {route_length}; + + #format {route_confirm} {[speedwalk] A route was found, type %c@dbspeedwalk_link{} to fast travel. [%c%g steps]<099>} {Green}{Cyan}{$route_length}; + #echo {$route_confirm}; +}; + +#ALIAS {^db$} { + #showme {[speedwalk] Try "db help" for usage instructions.<099>}; +}; + +#ALIAS {db help} { + #showme {[speedwalk] Speedwalk and Database Lookup Commands:<099>}; + #showme { <138>* "db room " - search for a room by name<099>}; + #showme { <138>* "db item " - search for an item in shops by name<099>}; + #showme { <138>* "db npc " - search for an NPC by name<099>}; + #showme { <138>* "db npcitem " - search for an item in NPC inventories by name<099>}; + #showme { <138>* "db gatherable " - search for a gatherable item by name<099>}; + #showme {<099>}; + #showme {After searching with one of the above commands, find a route there with "db route <#>", where "<#>" is the row number shown in the results. You can then type "speedwalk" to start moving. Alternatively, mouse-click on a result to generate a route, then click "speedwalk" to begin moving.<099>}; + #showme {<099>}; + #showme {There's also a bookmark system for frequently used rooms, see "db bookmark help" for details.<099>}; + #showme {<099>}; + #showme {Tips:<099>}; + #showme { <138>* When searching, the "query" is case-sensitive.<099>}; + #showme { <138>* Searches may be filtered to certain maps by providing a second parameter like "\{map_name\}".<099>}; + #showme { <138>* Example, to find swords in the Ramtops, you could "db item sword \{ramtops\}".<099>}; + #showme { <138>* Valid map names include:<099>}; + #showme { <138>* am/ankh-morpork <138>* bp/bes pelargic<099>}; + #showme { <138>* djb/djelibeybi <138>* ephebe<099>}; + #showme { <138>* genua <138>* ramtops<099>}; + #showme { <138>* sto/sto plains <138>* sto-lat <099>}; +}; + +#nop *** BOOKMARK SYSTEM IMPLEMENTATION ***; +#ALIAS {^db bookmark help$} { + #showme {[speedwalk] Speedwalk Bookmark System Commands:<099>}; + #showme { <138>* "db bookmark add here" - add current location to bookmarks.<099>}; + #showme { <138>* "db bookmark list" - show a list of saved locations.<099>}; + #showme { <138>* "db bookmark remove <#>" - remove bookmark at index..<099>}; + #showme {<099>}; + #showme {Generate a route from your current location to a bookmark using "db bookmark route <#>" or mouse-clicking on a result from "db bookmark list". You can then click or type "speedwalk to start moving.<099>}; +}; + +#nop sync database speedwalk bookmarks from file on load; +#class {dbsync} {clear}; +#class {dbsync} {read} {$dbbookmark_file}; +#variable {db_bookmark_list} {$bookmark_sync}; +#class {dbsync} {kill}; + +#ALIAS {^db bookmark list$} { + #showme {[speedwalk] Speedwalk Saved Bookmarks:<099>}; + #list {db_bookmark_list} {size} {db_bookmark_size}; + #if {$db_bookmark_size > 0} { + #var {bookmark_count} {0}; + #foreach {$db_bookmark_list[%*]} {bookmark_result} { + #math {bookmark_list_index} {$bookmark_count + 1}; + #variable {bookmark_room_id} {$db_bookmark_list[$bookmark_list_index][id]}; + #showme { [@dbrouteto_link{$bookmark_room_id;$bookmark_count}]: Room "@dbrouteto_link{$bookmark_room_id;$bookmark_result[name]}" in $bookmark_result[map] ($bookmark_result[type]).<099>}; + #math {bookmark_count} {$bookmark_count + 1}; + }; + }; + #else { + #showme { No saved bookmarks found, add one with "db bookmark add here" or refer to "db bookmark help".<099>}; + }; +}; +#ALIAS {^db bookmark add here$} { + #nop first - are we at our bookmark capacity?; + #list {db_bookmark_list} {size} {db_bookmark_size}; + #if {$db_bookmark_size > 19} { + #showme {[speedwalk] Maximum allowed number of bookmarks already set, remove one first.<099>}; + #return; + }; + + #nop next - do we already have this place bookmarked?; + #foreach {$db_bookmark_list[%*]} {bookmark_result} { + #if {"$bookmark_result[id]" == "$GMCP[room][info][identifier]"} { + #showme {[speedwalk] This room is already bookmarked and entitled "$bookmark_result[name]".<099>}; + #return; + }; + }; + + #nop TODO if we decide to allow nicknames then need to check for duplicates there as well; + #nop TODO saving class to file each time we add/remove so it persists; + + #nop some places don't have a "name" (e.g. Medina or Shades), don't allow bookmarking those for now.; + #if {"$GMCP[room][info][name]" == ""} { + #showme {[speedwalk] This room is not able to be bookmarked.<099>}; + #return; + }; + + #nop go to the database to get a MapID to store; + #format {query} {SELECT room_id, map_id, room_short, room_type FROM rooms WHERE room_id = '%s' LIMIT 1;} {$GMCP[room][info][identifier]}; + #format {db_command} {sqlite3 -separator ";" src/quow.db "%s"} {$query}; + #script {room_query_result} {$db_command}; + #format {room_query_result} {$room_query_result[1]}; + #replace {room_query_result} {\\} {}; + #list {room_results} {create} {${room_query_result}}; + + #variable {room_info} { + {room_id}{$room_results[1]} + {map_name}{$map_id_names[$room_results[2]]} + {room_short}{$room_results[3]} + {room_type}{$room_results[4]} + }; + + #nop finally put the new bookmark in our list; + #list {db_bookmark_list} {add} {{ + {id}{$room_info[room_id]} + {name}{$room_info[room_short]} + {map}{$room_info[map_name]} + {type}{$room_info[room_type]} + }}; + #showme {[speedwalk] Added a new bookmark for "$GMCP[room][info][name]" in $room_info[map_name] ($room_info[room_type]).<099>}; + /writedbbookmarkstofile; +}; +#ALIAS {^db bookmark remove %d$} { + #math {bookmark_removal_index} {%1 + 1}; + + #nop is this index in valid range?; + #list {db_bookmark_list} {size} {db_bookmark_size}; + #if {$db_bookmark_size < $bookmark_removal_index} { + #showme {[speedwalk] No bookmark with number "%1" set, check "db bookmark list".<099>}; + #return; + }; + + #nop otherwise seems valid, so remove it; + #list {db_bookmark_list} {get} {$bookmark_removal_index} {removed_bookmark}; + #list {db_bookmark_list} {delete} {$bookmark_removal_index}; + #showme {[speedwalk] Removed an existing bookmark for "$removed_bookmark[name]" at index "$bookmark_removal_index".<099>}; + /writedbbookmarkstofile; +}; +#ALIAS {^db bookmark route %d$} { + #math {bookmark_route_index} {%1 + 1}; + + #nop is this index in valid range?; + #list {db_bookmark_list} {size} {db_bookmark_size}; + #if {$db_bookmark_size < $bookmark_route_index} { + #showme {[speedwalk] No bookmark with number "%1" set, check "db bookmark list".<099>}; + #return; + }; + + #nop otherwise seems valid, so lets route to it; + db routeto $db_bookmark_list[$bookmark_route_index][id]; +}; +#ALIAS {^/writedbbookmarkstofile} { + #class {dbsync} {clear}; + #class {dbsync} {open}; + #var {bookmark_sync} {$db_bookmark_list}; + #class {dbsync} {close}; + #class {dbsync} {write} {$dbbookmark_file}; +}; +#nop *** END BOOKMARK SYSTEM IMPLEMENTATION ***; + + +#nop ===[ Handle Events for Clickable Links... for the newbs ]===; +#nop handle DBROUTE click events; +#EVENT {PRESSED SECURE LINK DBROUTE MOUSE BUTTON ONE} { + db route %4; +}; +#EVENT {PRESSED SECURE LINK DBROUTETO MOUSE BUTTON ONE} { + db routeto %4; +}; +#nop handle SPEEDWALK click events; +#EVENT {PRESSED SECURE LINK DBSPEEDWALK MOUSE BUTTON ONE} { + speedwalk; +}; + +#nop ===[ Functions to generate the clickable links ]===; +#FUNCTION {dbroute_link} { + #nop %1 = query result index, %2 = text to display the link as; + #return {\e]68;2;DBROUTE;%1\a\e[4;24m%2\e[24m}; +}; +#FUNCTION {dbrouteto_link} { + #nop %1 = id to route to, %2 = text to display the link as; + #return {\e]68;2;DBROUTETO;%1\a\e[4;24m%2\e[24m}; +}; +#FUNCTION {dbspeedwalk_link} { + #return {\e]68;2;DBSPEEDWALK;;\a\e[4mspeedwalk\e[24m}; +}; + #CLASS {database} {close}; diff --git a/src/deadletter.tin b/src/deadletter.tin deleted file mode 100644 index 630c1c0..0000000 --- a/src/deadletter.tin +++ /dev/null @@ -1,861 +0,0 @@ -#CLASS {deadletter} {kill}; -#CLASS {deadletter} {open}; - -#var {letter_difficulty} {none}; - -#VAR {difficult_customer_targets} { - {Triad Boss}{ - {show}{Triad [Boss]} - {search}{Triad boss} - } - {Queen Shelox}{ - {show}{Queen [Shelox]} - {search}{false} - } - {ice giants}{ - {show}{Ice Giants} - {search}{false} - } - {Security guards}{ - {show}{First Imperial Bank Security Guards} - {search}{false} - } - {Smuggler Captain}{ - {show}{Smuggler Captain} - {search}{false} - } - {The Clown}{ - {show}{Ha'pennywise the Clown} - {search}{false} - } - {Hotan Hwoar Ni}{ - {show}{[Hotan] Hwoar Ni (Inner Snail)} - {search}{Hotan} - } - {Thursday}{ - {show}{Thursday} - {search}{false} - } - {The Grflx}{ - {show}{The [Grflx]} - {search}{false} - } - {Vyrt}{ - {show}{Vyrt} - {search}{Vyrt} - } - {Bug}{ - {show}{Bug, Ramtops} - {search}{false} - } - {Avalanche Preventore}{ - {show}{Avalance Preventore, Endless Halls} - {search}{false} - } -}; - -#VAR {distant_land_targets} { - {Boy Willie}{ - {show}{Boy [Willie]} - {search}{Willie} - } - {Disembowel-Meself-Honourably Dibhala}{ - {show}{Disembowel-Meself-Honourably [Dibhala]} - {search}{false} - } - {Glod-san}{ - {show}{[Glod-san]} - {search}{false} - } - {Laggy-san}{ - {show}{[Laggy-san]} - {search}{false} - } - {Lon Fah Lo}{ - {show}{Lon Fah [Lo]} - {search}{false} - } - {Mad Hamish}{ - {show}{Mad [Hamish]} - {search}{false} - } - {No Go Wan}{ - {show}{No Go [Wan]} - {search}{false} - } - {Quisoda}{ - {show}{[Quisoda]} - {search}{false} - } - {Ruto of Fish}{ - {show}{[Ruto] of Fish} - {search}{false} - } - {Ryattenoki}{ - {show}{[Ryattenoki]} - {search}{false} - } - {Shi Do Gai}{ - {show}{Shi Do [Gai]} - {search}{false} - } - {Lap-lip}{ - {show}{[Lap-lip]} - {search}{false} - } - {Lip-phon Lap-top}{ - {show}{Lip-phon [Lap-top]} - {search}{false} - } - {Mihk-gran-bohp}{ - {show}{Mihk-gran-[bohp]} - {search}{false} - } - {Phos-phor}{ - {show}{[Phos]-phor} - {search}{false} - } - {Very Reverend Khepresh}{ - {show}{Very Reverend [Khepresh]} - {search}{false} - } - {Yclept}{ - {show}{[Yclept]} - {search}{false} - } - {Tuffy}{ - {show}{[Tuffy]} - {search}{false} - } - {Casanunda}{ - {show}{[Casanunda]} - {search}{false} - } - {Greig Schwitz}{ - {show}{Greig [Schwitz]} - {search}{false} - } - {Noobie}{ - {show}{[Noobie]} - {search}{false} - } - {Tfat Chick}{ - {show}{Tfat [Chick]} - {search}{false} - } - {Cut Me Own Hand Off Dhblah}{ - {show}{Cut Me Own Hand Off [Dhblah]} - {search}{false} - } - {Miss Pennie Laced}{ - {show}{Miss Pennie [Laced]} - {search}{false} - } - {Berti Boggis}{ - {show}{[Berti] Boggis} - {search}{false} - } - {The Proprietor}{ - {show}{The Proprietor} - {search}{false} - } -}; - -#VAR {far_away_targets} { - {Cern Smith}{ - {show}{[Cern] Smith} - {search}{Cern Smith} - } - {Granny Weatherwax}{ - {show}{Granny [Weatherwax]} - {search}{Weatherwax} - } - {Gulta Smith}{ - {show}{[Gulta] Smith} - {search}{Gulta} - } - {Hanlo Shepston}{ - {show}{[Hanlo] Shepston} - {search}{Hanlo} - } - {Jaims Smith}{ - {show}{[Jaims] Smith} - {search}{Jaims} - } - {Kev Ogg}{ - {show}{[Kev] Ogg} - {search}{Kev} - } - {Mr. Ogg}{ - {show}{Mr. Ogg} - {search}{false} - } - {Martha Headlock}{ - {show}{[Martha] Headlock} - {search}{Martha} - } - {Mrs Smith}{ - {show}{Mrs Smith} - {search}{false} - } - {Cohen the Barbarian}{ - {show}{Cohen the Barbarian} - {search}{Cohen} - } - {Morchella Esculenta}{ - {show}{[Morchella] Esculenta} - {search}{Morchella} - } - {Emile}{ - {show}{Emile} - {search}{Emile} - } - {Fern Golly}{ - {show}{[Fern] Golly} - {search}{Fern} - } - {Granville}{ - {show}{Granville} - {search}{Granville} - } - {Virgo Vango}{ - {show}{[Virgo] Vango} - {search}{Virgo} - } - {The Proprietor}{ - {show}{The Proprietor} - {search}{} - } - {shopkeeping native}{ - {show}{shopkeeping native} - {search}{shopkeeping native} - } - {Scuttle Me Own Canoe Dihbl}{ - {show}{Scuttle Me Own Canoe [Dihbl]} - {search}{Dihbl} - } - {SMOC Dihbl}{ - {show}{Scuttle Me Own Canoe Dihbl} - {search}{Dihbl} - } - {Dia Shale}{ - {show}{Dia [Shale]} - {search}{Shale} - } - {Esther Elderbury}{ - {show}{[Esther] Elderbury} - {search}{Esther} - } - {DEATH}{ - {show}{DEATH} - {search}{Death} - } - {Dil the Embalmer}{ - {show}{Dil the Embalmer} - {search}{Dil} - } - {Khip-en-dahl}{ - {show}{Khip-en-dahl} - {search}{Khip-en-dahl} - } - {Master Bibh-lat}{ - {show}{Master Bibh-Lat} - {search}{Bibh} - } - {Merk}{ - {show}{Merk} - {search}{Merk} - } - {Ptep-ptip-ptop}{ - {show}{Ptep-ptip-ptop} - {search}{Ptep} - } - {Ptik-ptak-ptoe}{ - {show}{Ptik-ptak-[ptoe]} - {search}{ptoe} - } - {Pugh Alighieri}{ - {show}{[Pugh] Alighieri} - {search}{Pugh} - } - {Teh-takk-eht}{ - {show}{[Teh]-takk-eht} - {search}{Teh} - } - {Toohumi}{ - {show}{Toohumi} - {search}{Toohumi} - } - {Ug Ogg}{ - {show}{Ug Ogg} - {search}{Ug Ogg} - } - {Wan-tew-iit}{ - {show}{Wan-tew-iit} - {search}{Wan-tew-iit} - } - {Arms trader}{ - {show}{Arms trader} - {search}{arms trader} - } - {Awaiting-Rain}{ - {show}{Awaiting-Rain} - {search}{Awaiting} - } - {Captain Ptargos}{ - {show}{Captain Ptargos} - {search}{Ptargos} - } - {Fis'n-tsips}{ - {show}{Fis'n-tsips} - {search}{tsips} - } - {One of them Dibblers with a long name}{ - {show}{One of them Dibblers with a long name} - {search}{Dih-bah-lah} - } - {May-I-be-Dipped-in-My-Own-Grease Dih-bah-lah}{ - {show}{May-I-be-Dipped-in-My-Own-Grease Dih-bah-lah} - {search}{Dih-bah-lah} - } - {Abraxas}{ - {show}{Abraxas} - {search}{Abraxas} - } - {Ania}{ - {show}{Ania} - {search}{Ania} - } - {Arimasticopoulous the Mosaicist}{ - {show}{Arimasticopoulous the Mosaicist} - {search}{Arimasticopoulous} - } - {Aurinax}{ - {show}{Aurinax} - {search}{Aurinax} - } - {Betty}{ - {show}{Betty} - {search}{Betty} - } - {Endos the Listener}{ - {show}{Endos the Listener} - {search}{Endos} - } - {Kakia}{ - {show}{Kakia} - {search}{Kakia} - } - {Klepton}{ - {show}{Klepton} - {search}{Klepton} - } - {Maintenance man}{ - {show}{Maintenance man} - {search}{maintenance} - } - {Master Zosimos}{ - {show}{Master Zosimos} - {search}{Zosimos} - } - {Rimasticopoulous the Mosaicist}{ - {show}{Rimasticopoulous the Mosaicist} - {search}{Rimasticopoulous} - } - {Souvlakios}{ - {show}{Souvlakios} - {search}{Souvlakios} - } - {The owner}{ - {show}{The owner [Spiros]} - {search}{Spiros} - } - {Xenophobios}{ - {show}{Xenophobios} - {search}{Xenophobios} - } - {Clara}{ - {show}{Clara} - {search}{Clara} - } - {Ema Nymton}{ - {show}{Ema Nymton} - {search}{Ema Nymton} - } - {Ivor von Kaeyya}{ - {show}{Ivor von Kaeyya} - {search}{Ivor} - } - {SMOH Dybbler}{ - {show}{SMOH Dybbler} - {search}{Dybbler} - } - {Trau Mirnicht}{ - {show}{Trau Mirnicht} - {search}{Trau} - } - {Verkauft Keinfleisch}{ - {show}{Verkauft Keinfleisch} - {search}{Verkauft} - } - {Generic employee}{ - {show}{A generic Charre Bux employee} - {search}{generic} - } - {Ayna}{ - {show}{Ayna} - {search}{Ayna} - } - {Blloddwyn}{ - {show}{Blloddwyn} - {search}{Blloddwyn} - } - {Brittnee}{ - {show}{Brittnee} - {search}{Brittnee} - } - {Captain Merreaux Vingian}{ - {show}{Captain Merreaux Vingian} - {search}{Merreaux} - } - {Cogsworth}{ - {show}{Cogsworth} - {search}{Cogsworth} - } - {David Leatherman}{ - {show}{David Leatherman} - {search}{Leatherman} - } - {Edna Scum}{ - {show}{Edna Scum} - {search}{Edna} - } - {Fredd S'Vage}{ - {show}{Fredd S'Vage} - {search}{Fredd} - } - {Harbour Master}{ - {show}{Harbour Master} - {search}{harbour master} - } - {Harbour mistress}{ - {show}{Harbour mistress} - {search}{harbour mistress} - } - {Hotel Reception}{ - {show}{Hotel Reception} - {search}{false} - } - {Jolly Roger}{ - {show}{Jolly Roger} - {search}{Jolly Roger} - } - {Mamie Olivia}{ - {show}{Mamie Olivia} - {search}{Olivia} - } - {Mr Ray}{ - {show}{Mr Ray} - {search}{Ray} - } - {Mr Myrtle}{ - {show}{Mr Myrtle} - {search}{Myrtle} - } - {Myrtle}{ - {show}{Myrtle} - {search}{Myrtle} - } - {Mrs Gogol}{ - {show}{Mrs Gogol} - {search}{Gogol} - } - {Sno-ball vendor}{ - {show}{Sno-ball vendor} - {search}{sno-ball} - } - {Spritz Peltanque}{ - {show}{Spritz Peltanque} - {search}{Spritz} - } - {Talula}{ - {show}{Talula} - {search}{Talula} - } - {The shopkeeper}{ - {show}{The shopkeeper [chandlery, Genua]} - {search}{false} - } - {The tipster}{ - {show}{The tipster} - {search}{tipster} - } - {Thom Cru'ez}{ - {show}{Thom Cru'ez} - {search}{Thom} - } - {Watch clerk}{ - {show}{Watch clerk} - {search}{false} - } - {Wounded soldier}{ - {show}{Wounded soldier} - {search}{false} - } - {Private Inside Leg}{ - {show}{Private Inside Leg} - {search}{Private} - } - {Monster}{ - {show}{Monster} - {search}{Monster} - } - {Bestiality Carter}{ - {show}{Bestiality Carter} - {search}{Bestiality} - } - {Brother Turbil}{ - {show}{Brother Turbil} - {search}{Turbil} - } - {Embezile Thatcher}{ - {show}{Embezile Thatcher} - {search}{Embezile} - } - {J. Ogg}{ - {show}{J. Ogg} - {search}{Jason} - } - {giant leader}{ - {show}{giant leader} - {search}{giant leader} - } - {Len}{ - {show}{Len} - {search}{Len} - } - {Maggie}{ - {show}{Maggie} - {search}{Maggie} - } - {Nicholas Winston}{ - {show}{Nicholas Winston} - {search}{Nicholas} - } - {Serb}{ - {show}{Serb Unglish} - {search}{Serb} - } - {Sharn}{ - {show}{Sharn Unglish} - {search}{Sharn} - } - {Our Wullie}{ - {show}{Our Wullie} - {search}{Wullie} - } - {Stephan}{ - {show}{Stephan} - {search}{Stephan} - } - {Young Steven}{ - {show}{Young Steven} - {search}{Young Steven} - } - {Suhm-wil-deih}{ - {show}{Suhm-wil-deih} - {search}{Suhm} - } - {Vic}{ - {show}{Vic} - {search}{Vic} - } - {Twoflower the tourist}{ - {show}{Twoflower the tourist} - {search}{Twoflower} - } - {My poor kidnapped daughter}{ - {show}{My poor kidnapped daughter} - {search}{captive} - } - {beautiful female captive}{ - {show}{beautiful female captive} - {search}{captive} - } - {Allen Kingfisher}{ - {show}{Allen Kingfisher} - {search}{Allen} - } - {Lobsang}{ - {show}{Abbot Lobsang} - {search}{Lobsang} - } - {Asap (Brother Asap)}{ - {show}{Asap (Brother Asap)} - {search}{Asap} - } - {Brother Hotpot}{ - {show}{Brother Hotpot} - {search}{Hotpot} - } - {Morchella Esculenta}{ - {show}{Morchella Esculenta} - {search}{Morchella} - } - {Bi-Bi Effenne}{ - {show}{Sister Bi-Bi Effenne} - {search}{Bi-Bi} - } - {Ti-Ti Effenne}{ - {show}{Sister Ti-Ti Effenne} - {search}{Ti-Ti} - } - {Heinz Blauhimmel}{ - {show}{Heinz Blauhimmel} - {search}{Heinz} - } - {Lichtenstein}{ - {show}{Lichtenstein} - {search}{Lichtenstein} - } - {Mad Gammer Nudity}{ - {show}{Mad Gammer Nudity} - {search}{Nudify} - } - {Candice Little}{ - {show}{Candice Little} - {search}{Candice} - } - {Cleetus Patterson}{ - {show}{Cleetus Patterson} - {search}{Cleetus} - } - {Granma Muckloe}{ - {show}{Granma Muckloe} - {search}{Muckloe} - } - {Peggy Little}{ - {show}{Peggy Little} - {search}{Peggy} - } - {Ann Celeria}{ - {show}{Ann [Celeria]} - {search}{Celeria} - } - {Doctor Lancreman}{ - {show}{Doctor Lancreman} - {search}{Lancreman} - } - {Hardenfast}{ - {show}{Hardenfast} - {search}{Hardenfast} - } - {Honest Dachshund}{ - {show}{Honest Dachshund} - {search}{Dachshund} - } - {Frank Doberman}{ - {show}{Frank Doberman} - {search}{Doberman} - } - {Irkabod Stork}{ - {show}{Irkabod Stork} - {search}{Irkabod} - } - {Kortina Van Dryver}{ - {show}{Kortina Van Dryver} - {search}{Kortina} - } - {Miss Van Dryer}{ - {show}{Miss Van Dryer} - {search}{false} - } - {Mr Smith (Mr Shopkeeper)}{ - {show}{Mr Smith (Mr Shopkeeper)} - {search}{false} - } - {Mrs Van Carrot}{ - {show}{Mrs Van Carrot} - {search}{Van Carrot} - } - {My good friend at the post office}{ - {show}{My good friend at the post office (Slippery)} - {search}{false} - } - {One and Only Manclef}{ - {show}{One and Only Manclef} - {search}{Manclef} - } - {Shopkeeper Bunch}{ - {show}{Shopkeeper Bunch} - {search}{Shopkeeper} - } - {The Magistrate}{ - {show}{The Magistrate} - {search}{Magistrate} - } - {Magistrate Flathead}{ - {show}{Magistrate Flathead} - {search}{Magistrate} - } - {Young Bran}{ - {show}{Young Bran} - {search}{Young Bran} - } - {A maitre d'}{ - {show}{A maitre d'} - {search}{maitre} - } - {Fleppo}{ - {show}{Fleppo} - {search}{Fleppo} - } - {Isobel Kingdom}{ - {show}{Isobel Kingdom} - {search}{Isobel} - } - {71-Hour Ahmed}{ - {show}{71-hour Ahmed} - {search}{Ahmed} - } - {Temple Guardian}{ - {show}{Temple Guardian [templeguard]} - {search}{guardian} - } - {Fione mac Feegle}{ - {show}{Fione mac Feegle} - {search}{Fione} - } - {Abe}{ - {show}{Abe} - {search}{Abe} - } - {Innkeeper}{ - {show}{grim innkeeper - Slaughtered Lamb, Uberwald} - {search}{grim innkeeper} - } - {Spiegal}{ - {show}{Spiegal} - {search}{Spiegal} - } -}; - -#alias {/solveletter} { - #if {&{letter_lines[1]}} { - #var {current_match_line} {^$letter_lines[1]$}; - #replace {current_match_line} {_} {%.}; - - #nop Check against far away targets; - #if {"$letter_difficulty" == "none" || "$letter_difficulty" == "far"} { - #foreach {$far_away_targets[]} {target} { - #regex {$target} {$current_match_line} { - #format {letter_solution_line} {%c[FA] %c*%c Deliver to %c%s%c.} {bold blue} {bold yellow} {bold blue} {bold green} {$far_away_targets[$target][show]} {bold blue}; - #echo {$letter_solution_line}; - - #if {"$far_away_targets[$target][search]" != "false"} { - db npc $far_away_targets[$target][search]; - }; - }; - } - } - - #nop Check against distant lands targets; - #if {"$letter_difficulty" == "none" || "$letter_difficulty" == "distant"} { - #foreach {$distant_land_targets[]} {target} { - #regex {$target} {$current_match_line} { - #format {letter_solution_line} {%c[DL] %c*%c Deliver to %c%s%c.} {bold blue} {bold yellow} {bold blue} {bold green} {$distant_land_targets[$target][show]} {bold blue}; - #echo {$letter_solution_line}; - - #if {"$distant_land_targets[$target][search]" != "false"} { - db npc $distant_land_targets[$target][search]; - }; - }; - } - } - - #nop Check against difficult customer targets; - #if {"$letter_difficulty" == "none" || "$letter_difficulty" == "difficult"} { - #foreach {$difficult_customer_targets[]} {target} { - #regex {$target} {$current_match_line} { - #format {letter_solution_line} {%c[DC] %c*%c Deliver to %c%s%c.} {bold blue} {bold yellow} {bold blue} {bold green} {$difficult_customer_targets[$target][show]} {bold blue}; - #echo {$letter_solution_line}; - - #if {"$difficult_customer_targets[$target][search]" != "false"} { - db npc $difficult_customer_targets[$target][search]; - }; - }; - } - } - - #nop var {letter_difficulty} {none}; - }; -}; - - -#NOP ==[Halt Parsing Letter]== -#alias {/stopparsingletter} { - #unvar {parsing_dead_letter}; - /solveletter -}; - -#action {^Deliver by %1.$} { - #if {&{parsing_dead_letter}} { - /stopparsingletter - } -}; -#action {does not have anything written on it.} { - #if {&{parsing_dead_letter}} { - /stopparsingletter - } -}; -#action {Cannot find "letter", no match.} { - #if {&{parsing_dead_letter}} { - /stopparsingletter - } -}; -#action {You are too busy fighting to read at the moment.} { - #if {&{parsing_dead_letter}} { - /stopparsingletter - } -}; - - -#NOP ==[Catch-all to grab letter lines, store only lines with a '_']== -#action {^%*$} { - #if {&{parsing_dead_letter}} { - #regex {%1} {%*_%*} { - #list {letter_lines} {add} {%1}; - }; - } -} {9}; - - -#NOP ==[Alias to attempt solve of letter in inventory]== -#ALIAS {sl} { - #send {read letter}; - #var {parsing_dead_letter} {0}; - #list {letter_lines} {create}; -} - - -#NOP ==[Actions to catch Frank telling you which delivery type]== -#action {^Frank asks: So you want to try delivering a letter to someone far away then, %1\?} { - #nop far away; - #if {%1 == $GMCP[charinfo][capname]} { - #var {letter_difficulty} {far}; - }; -} -#action {^Frank asks: So you want to try delivering a letter to someone really far away then, %1\?} { - #nop distant land; - #if {%1 == $GMCP[charinfo][capname]} { - #var {letter_difficulty} {distant}; - }; -} -#action {^Frank exclaims: Sometimes we end up with letters addressed to people who we've had problems with before. They are not easy letters to deliver. Do you want to try delivering one? You'll have to do it in less than an hour. Think carefully now, %1, it could be dangerous!} { - #nop difficult; - #if {%1 == $GMCP[charinfo][capname]} { - #var {letter_difficulty} {difficult}; - }; -} - -#CLASS {deadletter} {close}; diff --git a/src/gmcp.tin b/src/gmcp.tin index 4590e13..d5fc8e1 100644 --- a/src/gmcp.tin +++ b/src/gmcp.tin @@ -14,55 +14,93 @@ #ALIAS {debug {on|off}} { - #IF {"%1" == "on"} { #VARIABLE {debug} {1}; }; - #ELSE { #VARIABLE {debug} {0} }; - #SHOWME { Debug is <139>$debug<099> }; + #IF {"%1" == "on"} { + #VARIABLE {debug} {1}; + #SHOWME {@debug_log{Debug is <139>on<099>.}}; + }; + #ELSE { + #VARIABLE {debug} {0}; + #SHOWME {@debug_log{Debug is <139>off<099>.}}; + }; #NOP; } #EVENT {IAC SB GMCP char.vitals IAC SE} { - #IF {"$debug" == "1"} { #SHOWME %1 }; + #IF {"$debug" == "1"} { + #SHOWME {@debug_log{%1}}; + }; #VAR GMCP[oldvitals] {$GMCP[vitals]}; #VAR GMCP[vitals] {%0}; #math {hploss} {$GMCP[vitals][hp] - $GMCP[oldvitals][hp]}; #if {$hploss < -150} { #format {hp_loss_display} {<139>HP IS FALLING! CAREFUL! (Lost %s HP)<099>} {$hploss}; - #echo {$hp_loss_display}; - #showme {\a\a}; - } + + #if {&GMCP[oldvitals][hp]} { + #echo {$hp_loss_display}; + #showme {\a}; + }; + }; #math {xpgain} {$GMCP[vitals][xp] - $GMCP[oldvitals][xp]}; #if {$xpgain > 5000} { #format {xp_gain_display} {(+<169>%g<099>xp)} {$xpgain}; - - #echo {$xp_gain_display}; - } + #delay {1} {#showme {$xp_gain_display};}; + }; update_prompt; } #EVENT {IAC SB GMCP char.info IAC SE} { - #IF {"$debug" == "1"} { #SHOWME %1 }; - #VAR GMCP[charinfo] {%0}; + #IF {"$debug" == "1"} { + #SHOWME {@debug_log{%1}}; + }; - #nop New login, invalidate stale info; + #nop We've SU'd, cleanup as appropriate; + + #nop delete group shields data; + #variable {group_shields} {}; + #variable {group_shields_current} {}; + /writegroupshieldstofile; + + #nop xpreset - do we still want to? if we dont mis-calculate on SU continued tracking is nice; + + #nop store data about new character; + #var GMCP[charinfo] {%0}; + + #nop invalidate stale info; + #unvar {GMCP[oldvitals]}; + #unvar {GMCP[vitals]}; + #var {latest_xp} {0}; + + #nop reset prompt details; + #var {tpa_status} {0}; + #var {tpa_color} {0}; + #nop #var {tpa_start} {0}; + #unvar {tpa_start}; #unvar {quota}; + + #nop re-split the screen to wipe out the prompt from last character; + #split {3}; } #EVENT {IAC SB GMCP room.info IAC SE} { - #IF {"$debug" == "1"} { #SHOWME %1 }; + #IF {"$debug" == "1"} { + #SHOWME {@debug_log{%1}}; + }; #VAR GMCP[room][info] {%0}; - /spottimercheckroomid $GMCP[room][info][identifier] + /spottimercheckroomid $GMCP[room][info][identifier]; } #EVENT {IAC SB GMCP room.map IAC SE} { - #IF {"$debug" == "1"} { #SHOWME %1 }; + #IF {"$debug" == "1"} { + #SHOWME {@debug_log{%1}}; + }; #VAR {minimap} {%0}; #REPLACE {minimap} {u001b} {}; #SYSTEM {rm logs/minimap.log && touch logs/minimap.log}; @@ -73,20 +111,21 @@ #EVENT {IAC SB GMCP room.writtenmap IAC SE} { - #IF {"$debug" == "1"} { #SHOWME %1 }; + #IF {"$debug" == "1"} { + #SHOWME {@debug_log{%1}}; + }; #VAR GMCP[room][mdt] {%0}; #REPLACE {GMCP[room][mdt]} {\\n} {}; #SYSTEM {rm logs/mapdoortext.log && touch logs/mapdoortext.log}; #LINE {log} {logs/mapdoortext.log} {$GMCP[room][mdt]}; + + /personal.gmcp.writtenmap $GMCP[room][info][identifier]; } #EVENT {IAC WILL GMCP} { #SEND {$IAC$DO$GMCP\}; - #nop #SEND {$IAC$SB$GMCP Core.Hello { "client": "$CLIENT_NAME", "version": "$CLIENT_VERSION" } $IAC$SE\}; - #nop #SEND {$IAC$SB$GMCP Core.Hello { "client": "TinTin++", "version": "2.01.2" } $IAC$SE\}; - #nop #SEND {$IAC$SB$GMCP Core.Supports.Set ["Char.Vitals", "char.info", "char.login", "room.info", "room.map", "room.writtenmap"] $IAC$SE\} - #SEND {$IAC$SB$GMCP core.hello { "client" : "TinTin++", "version" : "2.01.2" } $IAC$SE\}; + #SEND {$IAC$SB$GMCP core.hello { "client" : "TinTin++", "version" : "2.02.60" } $IAC$SE\}; #SEND {$IAC$SB$GMCP core.supports.set [ "char.login", "char.info", "char.vitals", "room.info", "room.map", "room.writtenmap" ] $IAC$SE\}; #nop; } @@ -97,4 +136,8 @@ #VARIABLE CLIENT_VERSION %1; } +#FUNCTION {debug_log} { + #format {result} {<<GMCP><099>> %0}; +}; + #CLASS gmcp close diff --git a/src/group.tin b/src/group.tin index 2c2e863..634c9cc 100644 --- a/src/group.tin +++ b/src/group.tin @@ -14,47 +14,77 @@ #nop ======================================================; #nop EFF -#ACTION {^ * %1 is floating around {him|her|it}:$} { - #variable {group_shields[$group_shields_current][eff]} {1}; - /writegroupshieldstofile +#ACTION {^ * %1 is floating around {him|her|it|you}:$} { + #if {"%2" == "you"} { + #variable {group_shields[$GMCP[charinfo][capname]][eff]} {1}; + }; + #else { + #variable {group_shields[$group_shields_current][eff]} {1}; + } + /softwritegroupshieldstofile; } #nop TPA #ACTION {^ * {She|He|It} is surrounded by a {(dull red |bright red |wobbling orange |flickering yellow |)?}magical impact shield\.$} { - #variable {group_shields[$group_shields_current][tpa]} {1}; - /writegroupshieldstofile + #if {"$group_shields_current" != ""} { + #variable {group_shields[$group_shields_current][tpa]} {1}; + /softwritegroupshieldstofile; + }; } #nop CCC -#ACTION {^ * {Her|His|Its} skin has been {hardened|covered} {with|to}{a?} %4, although {the \w+|it} seems {weaker|not to be}} { - #variable {group_shields[$group_shields_current][ccc]} {1}; - /writegroupshieldstofile +#ACTION {^ * {Her|His|Its} skin {is|has been} {hardened|covered} {with|to}{a?} %4, although {the \w+|it} seems {weaker|not to be}} { + #if {"$group_shields_current" != ""} { + #variable {group_shields[$group_shields_current][ccc]} {1}; + /softwritegroupshieldstofile; + }; } +#ACTION {^ * Your skin {is|has been} {hardened|covered} {with|to}{a?} %4, although {the \w+|it} seems {weaker|not to be}} { + #variable {group_shields[$GMCP[charinfo][capname]][ccc]} {1}; + /softwritegroupshieldstofile; +}; #ACTION {^ * Tiny threads of metal run criss-cross all over {her|his|its} skin, although it seems weaker} { - #variable {group_shields[$group_shields_current][ccc]} {1}; - /writegroupshieldstofile + #if {"$group_shields_current" != ""} { + #variable {group_shields[$group_shields_current][ccc]} {1}; + /softwritegroupshieldstofile; + }; } #nop MS #ACTION {^ * {She|He|It} is {(really )?}protected by the power of {Pishe|Gufnork|Gapp|Sandelfon|Fish|Hat|Sek|Aegadon|Cubal|Reebox}} { - #variable {group_shields[$group_shields_current][ms]} {1}; - /writegroupshieldstofile + #if {"$group_shields_current" != ""} { + #variable {group_shields[$group_shields_current][ms]} {1}; + /softwritegroupshieldstofile; + }; +} +#ACTION {^ * You are {(really )?}protected by the power of {Pishe|Gufnork|Gapp|Sandelfon|Fish|Hat|Sek|Aegadon|Cubal|Reebox}} { + #variable {group_shields[$GMCP[charinfo][capname]][ms]} {1}; + /softwritegroupshieldstofile; } #nop KII -#ACTION {^ * {She|He|It} is surrounded by a {handful|cloud|small swarm|large swarm|vast sawrm|plague} of} { - #variable {group_shields[$group_shields_current][kii]} {1}; - /writegroupshieldstofile +#ACTION {^ * {You|She|He|It} {is|are} surrounded by a {handful|cloud|small swarm|large swarm|vast sawrm|plague} of} { + #if {"%1" == "You"} { + #variable {group_shields[$GMCP[charinfo][capname]][kii]} {1}; + }; + #else { + #variable {group_shields[$group_shields_current][kii]} {1}; + }; + /softwritegroupshieldstofile; }; #nop No shields at all #ACTION {^{(\w+\b\W*?){1,8}} has no arcane protection.$} { - #variable {group_shields[%1][ccc]} {0}; - #variable {group_shields[%1][tpa]} {0}; - #variable {group_shields[%1][eff]} {0}; - #variable {group_shields[%1][kii]} {0}; - #variable {group_shields[%1][ms]} {0}; - /writegroupshieldstofile + #if {"$group_shields_current" != ""} { + #if {"%1" != "%+ the %+ horse"} { + #variable {group_shields[%1][ccc]} {0}; + #variable {group_shields[%1][tpa]} {0}; + #variable {group_shields[%1][eff]} {0}; + #variable {group_shields[%1][kii]} {0}; + #variable {group_shields[%1][ms]} {0}; + /softwritegroupshieldstofile; + }; + }; }; @@ -66,16 +96,17 @@ #ACTION {[%1] {[A-Za-z]+} has left the group.} { #if {&group_shields[%2]} { #unvariable {group_shields[%2]}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; #ACTION {[%1] {[A-Za-z]+} has joined the group.} { + #tab {%2}; #variable {group_shields[%2]} {{tpa}{0}{ccc}{0}{eff}{0}{ms}{0}{kii}{0}}; - /writegroupshieldstofile + /softwritegroupshieldstofile; }; #ACTION {[%1] You have left the group.} { #var {group_shields} {}; - /writegroupshieldstofile + /writegroupshieldstofile; }; #ACTION {[%1] You have joined the group.} { #var {group_shields} {}; @@ -94,17 +125,38 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][eff]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; #ACTION {^In blocking the attack {(?:.+)} floating around {(?:\w+\b\W*?){1,8}} is knocked out of orbit.$} { + #nop Record room of drop even for non group mates... might come in handy; + #variable {group_eff_item} {%1}; + #variable {group_eff_target} {%2}; + #variable {group_eff_room} {$GMCP[room][info][name]}; + #variable {gs_dropname} {%2}; #replace {gs_dropname} { } {;}; #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][eff]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; + + #if {"$gs_eff_alerts" == "1"} { + #showme {\a}; + }; + + #return @log_line_to_chat{}; + }; + #elseif {"$name" == "you"} { + #variable {group_shields[$GMCP[charinfo][capname]][eff]} {0}; + /writegroupshieldstofile; + + #if {"$gs_eff_alerts" == "1"} { + #showme {\a}; + }; + + #return @log_line_to_chat{}; }; }; }; @@ -114,7 +166,7 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][eff]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -123,16 +175,17 @@ #nop TPA #ACTION {^There is a sudden white flash around {(?!a passing wizard)(\w+\b\W*?){1,8}}.$} { #showme {\a}; - #line {log} {$chat_file}; #variable {gs_dropname} {%1}; #replace {gs_dropname} { } {;}; #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][tpa]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; + + #return @log_line_to_chat{}; }; #ACTION {^With a noise that sounds like "Plink!", the air around {(\w+\b\W*?){1,8}} flashes {yellow|red} for a moment.$} { #variable {gs_dropname} {%1}; @@ -140,7 +193,7 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][tpa]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -153,7 +206,7 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][ccc]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -163,17 +216,17 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][ccc]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; -#ACTION {^{(\w+\b\W*?){1,8}}'s skin is now as {elastic|thicky covered|hard} as it can get.$} { +#ACTION {^{(\w+\b\W*?){1,8}}'s skin is now as {elastic|thickly covered|hard} as it can get.$} { #variable {gs_dropname} {%1}; #replace {gs_dropname} { } {;}; #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][ccc]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -183,7 +236,7 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][ccc]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -193,20 +246,28 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][ccc]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; +#ACTION {^With a brief flash of magic, your %1 skin falls away.$} { + #variable {group_shields[$GMCP[charinfo][capname]][ccc]} {0}; + /writegroupshieldstofile; +}; +#ACTION {^Your skin is {already|now} as {elastic|thickly covered|hard} as it can get.$} { + #variable {group_shields[$GMCP[charinfo][capname]][ccc]} {1}; + /writegroupshieldstofile; +}; -#nop KII +#nop KII; #ACTION {^The last of the injured {lacewings|stick insects|mayflies|praying mantids|butterflies|ladybirds|dragonflies|damselflies|moths|grasshoppers|winged termites|sandflies|mosquitoes|gnats|crickets|flying ants|locusts|horseflies|cicadas|bees|wasps|hornets|elephant beetles|assassin bugs} surrounding {(\w+\b\W*?){1,8}} crash to the ground.$} { #variable {gs_dropname} {%1}; #replace {gs_dropname} { } {;}; #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][kii]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -216,7 +277,7 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][kii]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; @@ -226,11 +287,21 @@ #foreach {$gs_dropname} {name} { #if {&group_shields[$name]} { #variable {group_shields[$name][kii]} {1}; - /writegroupshieldstofile + /writegroupshieldstofile; }; }; }; +#nop MS; +#ACTION {^You are protected by the power of {Pishe|Gufnork|Gapp|Sandelfon|Fish|Hat|Sek|Aegadon|Cubal|Reebox}.$} { + #variable {group_shields[$GMCP[charinfo][capname]][ms]} {1}; + /writegroupshieldstofile; +}; +#ACTION {^Your divine protection expires.$} { + #variable {group_shields[$GMCP[charinfo][capname]][ms]} {0}; + /writegroupshieldstofile; +}; + #nop ======================================; @@ -240,90 +311,128 @@ #ALIAS {rgs} { #variable {group_shields} {}; #variable {group_shields_current} {}; + /writegroupshieldstofile; #send {group shields}; - /writegroupshieldstofile }; #ALIAS {sgs} { - #foreach {$group_shields[]} {name} { + #showme {.:: Group Shields ::.}; + #foreach {*group_shields[]} {name} { #format {gs_namelength} {%L} {$name}; #math {gs_namelength} {9 - $gs_namelength}; #if {$gs_namelength < 0} { #var {gs_namelength} {0}; }; #format {gs_displayline} { * %.9s:%+${gs_namelength}s } {$name}; #foreach {tpa;ccc;eff;ms;kii} {shield} { - #if {&group_shields[$name][$shield] && "$group_shields[$name][$shield]" == "1"} { - #format {gs_displayline} {%s %s<099>} {$gs_displayline} {$shield}; - } { - #format {gs_displayline} {%s %s<099>} {$gs_displayline} {$shield}; - }; - }; - #showme {$gs_displayline}; - }; -}; - -#ALIAS {/writegroupshieldstofile} { - #system {rm $groupshield_file && touch $groupshield_file}; - - #line {log} {$groupshield_file} {.:: Group Shields ::. \n\r}; - #foreach {$group_shields[]} {name} { - #format {gs_namelength} {%L} {$name}; - #math {gs_namelength} {6 - $gs_namelength}; - #nop #if {$gs_namelength < 0} { #var {gs_namelength} {0}; }; - #format {gs_displayline} { %.6s:%+${gs_namelength}s } {$name}; - - #foreach {tpa;ccc;eff;kii} {shield} { #if {&group_shields[$name][$shield] && "$group_shields[$name][$shield]" == "1"} { #format {gs_displayline} {%s %u<099>} {$gs_displayline} {$shield}; } { #format {gs_displayline} {%s %u<099>} {$gs_displayline} {$shield}; }; }; - #line {log} {$groupshield_file} {$gs_displayline}; + #showme {$gs_displayline}; + }; +}; +#VARIABLE {gs_eff_alerts} {1}; +#ALIAS {^gs eff on$} { + #VARIABLE {gs_eff_alerts} {1}; + #showme {[group] Group shield EFF bell alerts are <128>ON.<900>}; + #showme {[group] Syntax: 'gs eff '.<900>}; +}; +#ALIAS {^gs eff off$} { + #VARIABLE {gs_eff_alerts} {0}; + #showme {[group] Group shield EFF bell alerts are <118>OFF.<900>}; + #showme {[group] Syntax: 'gs eff '.<900>}; +}; +#ALIAS {^gs eff$} { + #IF {"$gs_eff_alerts" == "0"} { + #showme {[group] Group shield EFF bell alerts are <118>OFF.<900>}; + #showme {[group] Syntax: 'gs eff '.<900>}; + } { + #showme {[group] Group shield EFF bell alerts are <128>ON.<900>}; + #showme {[group] Syntax: 'gs eff '.<900>}; }; - #return; }; +#FORMAT {shieldfile_lastwrite} {%T}; +#MATH {shieldfile_lastwrite} {$shieldfile_lastwrite - 10}; +#ALIAS {/softwritegroupshieldstofile} { + #format {current_time} {%T}; + #math {shieldfile_lastdelta} {$current_time - $shieldfile_lastwrite}; + #if {$shieldfile_lastdelta < 2} { + #nop Don't bother writing if its been less than 5 seconds; + #delay {swgstf} {/softwritegroupshieldstofile;} {2}; + #return; + }; + + /writegroupshieldstofile; + #format {shieldfile_lastwrite} {%T}; +}; + +#ALIAS {/writegroupshieldstofile} { + #nop Delete and recreate an empty file; + #system {rm $groupshield_file && touch $groupshield_file}; + + #nop Log a title then iterate over group members to display line by line; + #line {log} {$groupshield_file} {.:: Group Shields ::.}; + #foreach {*group_shields[]} {name} { + #format {gs_namelength} {%L} {$name}; + #math {gs_namelength} {6 - $gs_namelength}; + #format {gs_displayline} {%.6s: } {$name}; + #format {gs_displayline} {%-8s} {$gs_displayline}; + + #foreach {tpa;ccc;eff;kii;ms} {shield} { + #if {&group_shields[$name][$shield] && "$group_shields[$name][$shield]" == "1"} { + #format {gs_displayline} {%s %u<099>} {$gs_displayline} {$shield}; + } { + #format {gs_displayline} {%s %u<099>} {$gs_displayline} {$shield}; + }; + }; + #line {log} {$groupshield_file} {$gs_displayline}; + }; + #format {shieldfile_lastwrite} {%T}; +}; + + +#nop =======================================================; +#nop ===[ Capture incidents and be able to report them ]===; +#nop =======================================================; +#ACTION {^The %1 grabs your %2. You struggle briefly but {she|he|it} wrests it from your grip and makes for a hasty retreat.} { + #variable {group_snatch_target} {%1}; + #variable {group_snatch_victim} {$GMCP[charinfo][capname]}; + #variable {group_snatch_item} {%2}; +}; +#ACTION {^The %1 and %2 struggle briefly over %3 before the %4 wrests it free%5$} { + #variable {group_snatch_target} {%1}; + #variable {group_snatch_victim} {%2}; + #variable {group_snatch_item} {%3}; +}; +#ACTION {^The %1 suddenly snatches %2 from %3 and makes a run for it.$} { + #variable {group_snatch_target} {%1}; + #variable {group_snatch_victim} {%3}; + #variable {group_snatch_item} {%2}; +}; +#ALIAS {gssnatch} { + #if {"$group_snatch_target" != "0"} { + #send {group say [snatch] $group_snatch_victim's "$group_snatch_item" was stolen by \{$group_snatch_target\}}; + } { + #showme {<188>[snatch] No recent snatch to report.<900>}; + }; +}; +#ALIAS {gseff} { + #if {"$group_eff_target" != "0"} { + #send {group say [eff] $group_eff_target's floating "$group_eff_item" was dropped in \{$group_eff_room\}}; + } { + #showme {<188>[eff] No recent EFF drops to report.<900>}; + }; +}; + + +#VARIABLE {group_snatch_target} {0}; +#VARIABLE {group_snatch_victim} {0}; +#VARIABLE {group_snatch_item} {0}; +#VARIABLE {group_eff_item} {0}; +#VARIABLE {group_eff_target} {0}; +#VARIABLE {group_eff_room} {0}; + #CLASS {group} {close}; - - - -#nop EFF for you; -#nop ^(?:> )?(?P(?:\w+\b\W*?){1,6}) floating around you breaks!$ -#nop ^(?:> )?You realise that (?P(?:\w+\b\W*?){1,6}) is no longer floating around you\.$ -#nop ^(?:> )?In blocking the attack (?P(?:\w+\b\W*?){1,6}) floating around you is knocked out of orbit\.$ -#nop ^(?:> )?(?P(?:\w+\b\W*?){1,6}) begins to float around you\.$ -#nop ^ \* (?P(?:\w+\b\W*?){1,6}) is floating around you\:$ - -#nop TPA for you -#nop ^(?:> )?There is a sudden white flash\. Your magical shield has broken\.$ -#nop ^(?:> )?With a noise that sounds like "Plink!", everything around you flashes (?>yellow|red) for a moment\.$ -#nop ^ \* You are surrounded by a (?P|dull red |bright red |wobbling orange |flickering yellow )magical impact shield\.$ - -#nop CCC for you -#nop ^(?:> )?With a brief flash of magic, your (?>metallic|stony|elastic) skin falls away\.$ -#nop ^(?:> )?Your skin feels itchy large pieces flake off as you scratch it\.$ -#nop ^(?:> )?The metallic network on your skin feels a fair bit stronger now\.$ -#nop ^(?:> )?Your? (?>feel your skin become (?>rock hard|elastic as rubber)|skin (?>feels (?>even harder now|much more elastic now)|is now as (?>hard|elastic|thickly covered) as it can get))\.$ -#nop ^ \* Tiny threads of metal run criss-cross all over your skin, although -#nop ^ \* Your skin (?>has been|has|is) (?>hardened to a rock-like form|hardened with numerous layers of a mineral-like substance|hardened with a chalk-like substance|covered with several layers of a chalk-like substance|covered with a thin layer of chalk|covered with a thick metal net|metal bands running all over it, forming a kind of net|covered with a thin metal net|covered with a thin, net-like metal coating|solidified into a rubberous form|made elastic with numerous layers of a rubber-like substance|treated with a latex-like substance|covered with several layers of a latex-like substance|covered with a thin layer of latex), although - - -#nop Major for you; -#nop ^(?:> )?Your divine protection expires\.$ -#nop ^(?:> )?You are protected by the power of (?>Pishe|Gufnork|Gapp|Sandelfon|Fish|Hat|Sek|Aegadon|Cubal|Reebox)\.$ -#nop ^ \* You are (?>(?>barely |really |perfectly )?protected|shielded) by the (?>power |protective armour |grace )of (?>Pishe|Gufnork|Gapp|Sandelfon|Fish|Hat|Sek|Aegadon|Cubal|Reebox)\. You will be protected for - - -#nop Bug shield for you; -#nop ^(?:> )?The last of the injured (?>lacewings|stick insects|mayflies|praying mantids|butterflies|ladybirds|dragonflies|damselflies|moths|grasshoppers|winged termites|sandflies|mosquitoes|gnats|crickets|flying ants|locusts|horseflies|cicadas|bees|wasps|hornets|elephant beetles|assassin bugs) surrounding you crash to the ground\.$ -#nop ^(?:> )?The (?>lacewings|stick insects|mayflies|praying mantids|butterflies|ladybirds|dragonflies|damselflies|moths|grasshoppers|winged termites|sandflies|mosquitoes|gnats|crickets|flying ants|locusts|horseflies|cicadas|bees|wasps|hornets|elephant beetles|assassin bugs) surrounding you scatter in different directions and fly off\.$ -#nop ^(?:> )?(?>Buzzing drowsily, t|Buzzing loudly, t|Buzzing threateningly, t|Clicking and buzzing, t|Whirring gently, t|Rather clumsily, t|With a rasping noise, t|With a high-pitched whining noise, t|With a low, menacing buzzing, t|With a buzzing sound so loud and meaty it's almost like a dog growling, t|T)he (?>handful|cloud|small swarm|large swarm|vast swarm|plague) of (?>lacewings|stick insects|mayflies|praying mantids|butterflies|ladybirds|dragonflies|damselflies|moths|grasshoppers|winged termites|sandflies|mosquitoes|gnats|crickets|flying ants|locusts|horseflies|cicadas|bees|wasps|hornets|elephant beetles|assassin bugs) (?>flutters into a loosely-formed orbit around |forms a chaotic web of small white bodies around |starts to hover near |begins to circle |begins to circle around |begins to orbit |begins to cluster around |begins to buzz erratically around |begins to buzz around |flutters into a chaotic formation around )you -#nop ^ \* You are surrounded by a (?>handful|cloud|small swarm|large swarm|vast swarm|plague) of - - - -#nop Arcane statuses -#nop ^(?:> )?Arcane protection status:$; - - diff --git a/src/macros.tin b/src/macros.tin index 0372de6..ccf9b3a 100644 --- a/src/macros.tin +++ b/src/macros.tin @@ -31,6 +31,6 @@ #macro {Oq} {southwest}; #nop 1; #macro {Or} {south}; #nop 2; #macro {Os} {southeast}; #nop 3; -#macro {Op} {id}; #nop 0; +#macro {Op} {enc}; #nop 0; #macro {On} {}; #nop .; #macro {Ol} {}; #nop +; diff --git a/src/magic.tin b/src/magic.tin index 066cac9..ffb053e 100644 --- a/src/magic.tin +++ b/src/magic.tin @@ -1,44 +1,44 @@ #CLASS {magic} {kill}; #CLASS {magic} {open}; -#NOP ==[Delude Levels]== -#SUB {^It has a faint octarine shadow about it that disappears if you look at it squarely.$} {%0 <858>(1/5)<898>}; -#SUB {^It has a faint octarine shadow about it.$} {%0 <858>(2/5)<898>}; -#SUB {^It has an octarine shadow about it that flickers occasionally out of the corner of your eye.$} {%0 <858>(3/5)<898>}; -#SUB {^It has a flickering octarine shadow about it.$} {%0 <858>(4/5)<898>}; -#SUB {^It has a flickering octarine haze about it.$} {%0 <858>(5/5)<898>}; +#NOP ==[Delude Levels]==; +#SUB {^It has a faint octarine shadow about it that disappears if you look at it squarely.$} {%0 <858>(1/5)<900>}; +#SUB {^It has a faint octarine shadow about it.$} {%0 <858>(2/5)<900>}; +#SUB {^It has an octarine shadow about it that flickers occasionally out of the corner of your eye.$} {%0 <858>(3/5)<900>}; +#SUB {^It has a flickering octarine shadow about it.$} {%0 <858>(4/5)<900>}; +#SUB {^It has a flickering octarine haze about it.$} {%0 <858>(5/5)<900>}; -#HIGH {^The octarine shadow around %* deepens%*} {bold magenta} -#HIGH {^The octarine shadow around %* fades%*} {magenta} +#HIGH {^The octarine shadow around %* deepens%*} {bold magenta}; +#HIGH {^The octarine shadow around %* fades%*} {magenta}; -#NOP ==[Item Enchantment Levels]== -#SUB {It occasionally pulses with octarine light} {%0 <858>(Lvl 1)<898>}; -#SUB {It emits a slight octarine glow} {%0 <858>(Lvl 2)<898>}; -#SUB {It softly pulses in dull octarine shades} {%0 <858>(Lvl 3)<898>}; -#SUB {It gives off a steady but dull octarine glow} {%0 <858>(Lvl 4)<898>}; -#SUB {It gives off a steady octarine glow} {%0 <858>(Lvl 5)<898>}; -#SUB {It glows an intense octarine} {%0 <858>(Lvl 6)<898>}; -#SUB {It emits a bright octarine colour} {%0 <858>(Lvl 7)<898>}; -#SUB {It brightly pulses octarine} {%0 <858>(Lvl 8)<898>}; -#SUB {It glows brilliant octarine shades} {%0 <858>(Lvl 9)<898>}; -#SUB {It radiates pure octarine brilliance} {%0 <858>(Lvl 10)<898>}; +#NOP ==[Item Enchantment Levels]==; +#SUB {It occasionally pulses with octarine light} {%0 <858>(Lvl 1)<900>}; +#SUB {It emits a slight octarine glow} {%0 <858>(Lvl 2)<900>}; +#SUB {It softly pulses in dull octarine shades} {%0 <858>(Lvl 3)<900>}; +#SUB {It gives off a steady but dull octarine glow} {%0 <858>(Lvl 4)<900>}; +#SUB {It gives off a steady octarine glow} {%0 <858>(Lvl 5)<900>}; +#SUB {It glows an intense octarine} {%0 <858>(Lvl 6)<900>}; +#SUB {It emits a bright octarine colour} {%0 <858>(Lvl 7)<900>}; +#SUB {It brightly pulses octarine} {%0 <858>(Lvl 8)<900>}; +#SUB {It glows brilliant octarine shades} {%0 <858>(Lvl 9)<900>}; +#SUB {It radiates pure octarine brilliance} {%0 <858>(Lvl 10)<900>}; -#NOP ==[Background Magic Levels]== -#SUB {There is the residual taste of magic in this place} {%0 <858>(50-149 thaums)<898>}; -#SUB {This place has seen some use of magic} {%0 <858>(150-299 thaums)<898>}; -#SUB {A considerable amount of magic has been used here} {%0 <858>(300-499 thaums)<898>}; -#SUB {A very large quantity of magic has been manipulated here} {%0 <858>(500-749 thaums)<898>}; +#NOP ==[Background Magic Levels]==; +#SUB {There is the residual taste of magic in this place} {%0 <858>(50-149 thaums)<900>}; +#SUB {This place has seen some use of magic} {%0 <858>(150-299 thaums)<900>}; +#SUB {A considerable amount of magic has been used here} {%0 <858>(300-499 thaums)<900>}; +#SUB {A very large quantity of magic has been manipulated here} {%0 <858>(500-749 thaums)<900>}; #NOP red; -#SUB {You can feel the Dungeon Dimensions trying to push in} {%0 <818>(750-1000 thaums)<898>}; +#SUB {You can feel the Dungeon Dimensions trying to push in} {%0 <818>(750-1000 thaums)<900>}; #NOP bold red!; -#SUB {Little sparks flash in from the Dungeon Dimensions} {%0 <118>(1001-1500 thaums)<098>}; -#SUB {Apparations of things with lots of tentacles seem to be on the edge of your vision} {%0 <118>(1501-2000 thaums)<098>}; -#SUB {So much magic has been expended here that the area is in danger of dumping itself into the Dungeon Dimensions} {%0 <118>(2001-5000 thaums)<098>}; +#SUB {Little sparks flash in from the Dungeon Dimensions} {%0 <118>(1001-1500 thaums)<900>}; +#SUB {Apparations of things with lots of tentacles seem to be on the edge of your vision} {%0 <118>(1501-2000 thaums)<900>}; +#SUB {So much magic has been expended here that the area is in danger of dumping itself into the Dungeon Dimensions} {%0 <118>(2001-5000 thaums)<900>}; -#NOP ==[PFG Damage Values]== +#NOP ==[PFG Damage Values]==; #SUB {^{The fire singes} } {%1 <019>(<<119>15<019>%)<099> }; #SUB {^{The fire burns} } {%1 <019>(<119>15<019>-<119>30<019>%)<099> }; #SUB {^{The fire crisps} } {%1 <019>(<119>30<019>-<119>60<019>%)<099> }; @@ -47,17 +47,35 @@ #SUB {^{The fire vaporises} } {%1 <019>(><119>90<019>%)<099> }; -#NOP ==[Replace Spell Casting]== -#ACTION {^You prepare to cast %1 {at|on} %2.$} { #showme { * <138>%1<098> -> %2}; #line gag} {3} -#ACTION {^You prepare to cast %1.$} { #showme { * <138>%1<098>}; #line gag} {4} +#NOP ==[Replace Spell Casting]==; +#ACTION {^You prepare to cast %1 {at|on} %2.$} { #showme { * <138>%1<099> -> %2}; #line gag} {3}; +#ACTION {^You prepare to cast %1.$} { #showme { * <138>%1<099>}; #line gag} {4}; -#NOP ==[Colour FCI Output]== -#SUB {The weave indicates that %1 is a %2, with %3 charge{s?} remaining.} {The weave indicates that <138>%1<098> is a <138>%2<098> with <158>%3<098> charges remaining.}; +#NOP ==[Colour FCI Output]==; +#SUB {The weave indicates that %1 is a %2, with %3 charge{s?} remaining.} {The weave indicates that <138>%1<900> is a <138>%2<900> with <158>%3<900> charges remaining.}; #NOP ==[Library/Distortions]== #HIGHLIGHT {^The area seems more mundane than before\.$} {green underscore}; #HIGHLIGHT {^{?>You notice an odd rippling in the air\.|The awful sound of nails being dragged down a blackboard fills the area briefly\.|A distortion in time and space is forming!}$} {red underscore}; +#NOP ==[Contemplate]==; +#SUB {^You focus your mind {once more on|on} %2.$} {You <188>focus<900> your mind %1 <138>%2<900>.}; +#SUB {^With %1 at the forefront of your mind, you enter a deep trance.$} {With <138>%1<900> at the forefront of your mind, you enter a deep <188>trance<900>.}; +#SUB {^You feel %1 sitting in your mind, eager to be fed again.$} {You feel <138>%1<900> sitting in your <188>mind<900>, eager to be <128>fed again<900>.}; +#SUB {^You have previously contemplated %1.$} {You have previously contemplated <138>%1<900>.}; +#SUB {^Your mind feels less crowded as %1 returns to its normal place.} {Your mind feels less <188>crowded<900> as <138>%1<900> returns to its <188>normal<900> place.}; +#HIGHLIGHT {^You are unconscious. You can't do anything.$} {}; +#HIGHLIGHT {^You emerge from your trance.$} {}; + +#NOP ==[Magic Items]==; +#SUB {^You feel you have regained enough strength to invoke a flame weapon again.$} {You feel you have regained enough strength to invoke<900> a flame<900> weapon again.}; +#ACTION {^You feel you have regained enough strength to invoke a flame weapon again.$} { #showme {\a}; }; +#SUB {^You zap your balsa wand at %1.$} {You zap<900> your balsa wand<900> at %1<900>.}; +#SUB {^A pulse of light leaves the balsa wand and envelops %1.$} {A pulse of light<900> leaves the balsa wand<900> and envelops %1<900>.}; +#SUB {^A pulse of pure night leaves the balsa wand and envelops you.$} {A pulse<900> of pure night<900> leaves the balsa wand<900> and envelops you<900>.}; +#SUB {^A pulse of pure night leaves the balsa wand and envelops you, while %1 fizzles slightly and dims.$} {A pulse<900> of pure night<900> leaves the balsa wand<900> and envelops you<900>, while the black diamond nostril stud<900> fizzles slightly and dims<900>.}; +#HIGHLIGHT {^You feel your skin start to ripple uncomfortably...$} {}; + #CLASS {magic} {close}; diff --git a/src/maproute.py b/src/maproute.py index a3454f8..cdcf3cd 100644 --- a/src/maproute.py +++ b/src/maproute.py @@ -73,9 +73,9 @@ class MapRoute: while bDone == False: # Loop through all the rooms we have yet to do iPreviousTotal = iTotalRooms - for iN in xrange(iNextRoom - 1, iPreviousTotal): + for iN in range(iNextRoom - 1, iPreviousTotal): # Loop through all exits from this room - for sKey, sExitData in self.exits_by_id[sDoRoom[iN]].iteritems(): + for sKey, sExitData in self.exits_by_id[sDoRoom[iN]].items(): # Make sure we aren't looping back around on ourselves, and that we haven't already finished if sKey != start_id and bEverDone.get(sKey) == None and iFinalRoom == 0: iTotalRooms = iTotalRooms + 1 @@ -131,7 +131,7 @@ class MapRoute: if iCurRoom == 1: bDone = True sRealPath = "" - for iN in xrange(len(sPath), 0, -1): + for iN in range(len(sPath), 0, -1): if sRealPath != "": sRealPath = "{};".format(sRealPath) sRealPath = "{}{}".format(sRealPath, sPath[iN-1]) diff --git a/src/mdt_matchcontrol.py b/src/mdt_matchcontrol.py new file mode 100644 index 0000000..8726640 --- /dev/null +++ b/src/mdt_matchcontrol.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python + +import re +import sys +import json + + +class MapDoorTextControl: + colour_map = { + # http://terminal-color-builder.mudasobwa.ru/ + "orange": "\033[01;38;05;214m", + "red": "\033[01;38;05;196m", + "cyan": "\033[01;38;05;37m", + "reset": "\033[00;39;49m" + } + + def __init__(self): + self.custom_matches = [] + + # Load any additional custom matches + with open('logs/features/mdt_custom_matches.json', 'r') as custom_file: + config = json.load(custom_file) + + # Strip comments from custom matches + for match in config: + if len(match) > 1: + self.custom_matches.append(match) + + def _write_file(self): + with open('logs/features/mdt_custom_matches.json', 'w') as custom_file: + json.dump(self.custom_matches, custom_file) + + def add_match(self, pattern, color="red", value=5, is_regex=False): + self.custom_matches.append([pattern, color, value, is_regex]) + self._write_file() + + def remove_match(self, pattern): + for match in self.custom_matches: + if match[0] == pattern or pattern in match[0]: + self.custom_matches.remove(match) + self._write_file() + + +if __name__ == '__main__': + if len(sys.argv) < 2: + #print('[error] Missing inputs. Usage: mdt_matchcontrol.py ') + print(0) + sys.exit() + + script, command, pattern = sys.argv.pop(0), sys.argv.pop(0), " ".join(sys.argv) + + mdtc = MapDoorTextControl() + if command == "add": + mdtc.add_match(pattern) + print(1) + elif command == "remove": + mdtc.remove_match(pattern) + print(1) + else: + #print('[error] Unrecognized command, valid commands are "add" and "remove".') + print(0) diff --git a/src/mdtparse.py b/src/mdtparse.py index 2eb67d0..b155595 100644 --- a/src/mdtparse.py +++ b/src/mdtparse.py @@ -61,6 +61,14 @@ class MapDoorText: "sw": "sw", "w": "w", "nw": "nw", + "fore": "f", + "aft": "a", + "starboard": "s", + "port": "p", + "port aft": "pa", + "port fore": "pf", + "starboard aft": "sa", + "starboard fore": "sf" } colour_map = { # http://terminal-color-builder.mudasobwa.ru/ @@ -78,6 +86,12 @@ class MapDoorText: with open('mdtconfig.json', 'r') as config_file: config = json.load(config_file) + # Load any additional custom matches + with open('logs/features/mdt_custom_matches.json', 'r') as custom_file: + additional_matches = json.load(custom_file) + + config['custom_matches'] = config['custom_matches'] + additional_matches; + # Strip comments from custom matches for match in config['custom_matches']: if len(match) > 1: @@ -179,6 +193,30 @@ class MapDoorText: elif entry.startswith("west"): is_direction = 1 this_direction = "west" + elif entry.startswith("port fore"): + is_direction = 1 + this_direction = "port fore" + elif entry.startswith("port aft"): + is_direction = 1 + this_direction = "port aft" + elif entry.startswith("starboard fore"): + is_direction = 1 + this_direction = "starboard fore" + elif entry.startswith("starboard aft"): + is_direction = 1 + this_direction = "starboard aft" + elif entry.startswith("port"): + is_direction = 1 + this_direction = "port" + elif entry.startswith("starboard"): + is_direction = 1 + this_direction = "starboard" + elif entry.startswith("fore"): + is_direction = 1 + this_direction = "fore" + elif entry.startswith("aft"): + is_direction = 1 + this_direction = "aft" if is_direction == 1: if not data['ignoring_exits']: diff --git a/src/mindspace/mindspace.tin b/src/mindspace/mindspace.tin new file mode 100644 index 0000000..fdb46ca --- /dev/null +++ b/src/mindspace/mindspace.tin @@ -0,0 +1,176 @@ +#class {mindspace} {kill}; +#class {mindspace} {open}; + +#nop ============================================================================= ; +#nop mindspace.tin - Mindspace calculation (spells in head, used/total/free) ; +#nop Reads spelldata.tin so spell_data is available. Use #read mindspace.tin to load. ; +#nop ============================================================================= ; + +#read {src/mindspace/spelldata.tin}; +#read {src/mindspace/spellbook.tin}; +#read {src/mindspace/skillscapture.tin}; +#read {src/mindspace/spellcheck.tin}; + +#nop --- Data: list of spell names currently memorized (filled by triggers in later steps) --- ; +#list {mindspace_spells} {create}; + +#nop --- State: current category, in-list flag, running totals, delay-finish control --- ; +#var {mindspace_category} {}; +#var {mindspace_category_display} {}; +#var {mindspace_in_list} {0}; +#var {mindspace_used_total} {0}; +#var {mindspace_category_used} {0}; +#var {mindspace_category_count} {0}; +#var {mindspace_pending_finish} {0}; +#var {mindspace_done} {0}; + +#nop --- Step 6/9: per-char data (max capacity, prev spell set). Key = GMCP[charinfo][capname]. Single var for persistence. --- ; +#var {mindspace_data} {}; +#var {mindspace_waiting_skills} {0}; + +#nop --- Load data from disk on load if we have a previous run --- ; +#variable {mindspace_file} {logs/features/mindspace_sync.tin}; +#class {mindsync} {clear}; +#class {mindsync} {read} {$mindspace_file}; +#variable {mindspace_data} {$mindspace_sync}; +#class {mindsync} {kill}; + +#nop --- Alias: capture max mindspace from "skills ma.sp.sp raw" (special bonus + 30). --- ; +#alias {^mindspace skills$} { + #var {mindspace_waiting_skills} {1}; + #var {mindspace_skills_done} {0}; + #send {cols 60}; + #send {skills ma.sp.sp raw}; + #send {skills $mindspace_skills_paths}; + #send {cols 999}; +}; + +#nop --- Alias: mindspace help -> show help text. --- ; +#alias {^mindspace help$} { + #showme {}; + #showme {Mindspace & Spellcheck Help<099>}; + #showme {}; + #showme {Command: "mindspace help"<099>}; + #showme { This help information}; + #showme {}; + #showme {Command: "spells"<099>}; + #showme { Show remembered spells with mindspace used/free and gained/lost since last run}; + #showme {}; + #showme {Command: "mindspace skills"<099>}; + #showme { Record your skill bonuses. Run after large skill or stat changes for accurate spellchecks}; + #showme {}; + #showme {Command: "mindspace "<099>}; + #showme { Lookup spell details, GP cost, mindspace, components, and full spellcheck data (e.g. mindspace tpa)}; + #showme {}; + #showme {Clickable links<099>}; + #showme { Book titles in spell output and spell acronyms in book listings are clickable.}; + #showme { Click a book title to list all spells in that book; click an acronym to show that spell.}; + #showme {}; +}; + +#nop --- Alias: mindspace -> show spell. (?!skills$|help$) so skills and help are not caught. --- +#alias {^mindspace {?!skills$|help$}{.*}$} { + #if {"%1" == ""} { #return }; + #format {mindspace_acr} {%l} {%1}; + #var {mindspace_full} {$acronym_to_spell[$mindspace_acr]}; + #if {"$mindspace_full" == ""} { + #showme {<118>No spell found for '%1'.<099>}; + #return; + }; + + #var {mindspace_show_spell_name} {$mindspace_full}; + /mindspace_show_spell; +}; + +#nop --- Action: parse magic.spells.special. Table format (configurable MUD output). %1 = bonus. --- ; +#action {^| | special......... %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { + #math {mindspace_data[$GMCP[charinfo][capname]][max]} {%2 + 30}; + /writemindspacetofile; + #delay {1} {#showme {Mindspace max set to $mindspace_data[$GMCP[charinfo][capname]][max] (raw bonus + 30).<099>}}; + }; +}; + +#nop --- Action: parse magic.spells.special. Raw/full-path format (other MUD output option). %1 = bonus. --- ; +#action {^magic\.spells\.special %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { + #math {mindspace_data[$GMCP[charinfo][capname]][max]} {%2 + 30}; + /writemindspacetofile; + #delay {1} {#showme {Mindspace max set to $mindspace_data[$GMCP[charinfo][capname]][max] (raw bonus + 30).<099>}}; + }; +}; + +#nop --- Alias: set cols 60, send "spells", enable spell-line action, reset state and memorized list --- ; +#alias {^spells$} { + #list {mindspace_spells} {clear}; + #var {mindspace_used_total} {0}; + #var {mindspace_done} {0}; + #var {mindspace_pending_finish} {0}; + #send {cols 60}; + + #nop Step 5: spell-line. Pattern = title-like line only (avoids Columns, prompt, digits). Alternatives: OR all spell names (huge regex), require two capitals, negative prefix (Columns|You |<). ; + #action {^{[A-Z][a-zA-Z'\- ]{10,}}$} { + #if {$mindspace_in_list} { + #foreach {*spell_data[%*]} {mindspace_spell} { + #if {"%%1" == "$mindspace_spell"} { + #list {mindspace_spells} {add} {$mindspace_spell}; + #math {mindspace_used_total} {$mindspace_used_total + $spell_data[$mindspace_spell][mindspace]}; + #math {mindspace_category_used} {$mindspace_category_used + $spell_data[$mindspace_spell][mindspace]}; + #math {mindspace_category_count} {$mindspace_category_count + 1}; + #break; + }; + }; + }; + }; + #nop Step 5: blank line. End-of-category summary, set pending and 1s delay. Unactioned in /mindspace_do_finish. ; + #action {^$} { + #if {$mindspace_in_list} { + #showme { $mindspace_category_display: $mindspace_category_used [${mindspace_category_count} spells]<099>}; + #var {mindspace_in_list} {0}; + #var {mindspace_pending_finish} {1}; + #undelay {mindspace_finish}; + #delay {mindspace_finish} {/mindspace_do_finish} {1}; + }; + }; + #send {spells}; + #send {cols 999}; +}; + +#nop --- Step 4: category header - set current category and enable line capture --- ; +#action {^You know the following {defensive|offensive|misc} magic spell{s?}:$} { + #var {mindspace_pending_finish} {0}; + #var {mindspace_category} {%1}; + #format {mindspace_category_display} {%n} {%1}; + #var {mindspace_in_list} {1}; + #var {mindspace_category_used} {0}; + #var {mindspace_category_count} {0}; +}; + +#nop --- Finish: only called when named delay fires (1s after last blank). Categories vary (1-3) so we always use delay. --- ; +#nop Step 8: /spellbook_do_gained_lost (in spellbook.tin) does diff, report, save. --- ; +#alias {/mindspace_do_finish} { + #undelay {mindspace_finish}; + #unaction {^{[A-Z][a-zA-Z'\- ]{10,}}$}; + #unaction {^$}; + #list {mindspace_spells} {size} {mindspace_spell_count}; + #if {"$mindspace_data[$GMCP[charinfo][capname]][max]" != "" && "$mindspace_data[$GMCP[charinfo][capname]][max]" != "0"} { + #math {mindspace_free} {$mindspace_data[$GMCP[charinfo][capname]][max] - $mindspace_used_total}; + #showme {Total Mindspace: $mindspace_used_total / $mindspace_data[$GMCP[charinfo][capname]][max] [${mindspace_spell_count} spells, $mindspace_free free]<099>}; + } { + #showme {Total Mindspace: $mindspace_used_total [${mindspace_spell_count} spells]<119> (run 'mindspace skills' to set total)<099>}; + }; + /spellbook_do_gained_lost; + /writemindspacetofile; + #var {mindspace_pending_finish} {0}; +}; + +#nop --- Alias to write out mindspace data to file so we can recall it later --- ; +#alias {^/writemindspacetofile$} { + #class {mindsync} {kill}; + #class {mindsync} {open}; + #var {mindspace_sync} {$mindspace_data}; + #class {mindsync} {close}; + #class {mindsync} {write} {$mindspace_file}; +}; + +#class {mindspace} {close}; diff --git a/src/mindspace/skillscapture.tin b/src/mindspace/skillscapture.tin new file mode 100644 index 0000000..e59fbee --- /dev/null +++ b/src/mindspace/skillscapture.tin @@ -0,0 +1,248 @@ +#class {mindspace_skills} {kill}; +#class {mindspace_skills} {open}; + +#nop ============================================================================= ; +#nop skillscapture.tin - Per-skill actions to capture bonuses (Phase 2 Step 1). ; +#nop Two actions per skill: table (branched) and raw path. Read after spelldata so skill_tree exists. ; +#nop ============================================================================= ; + +#nop --- Comma-separated paths to request in one "skills" command (all skill_tree paths). --- ; +#var {mindspace_skills_paths} {ma.me.el.air,ma.me.el.earth,ma.me.el.fire,ma.me.el.water,ma.me.me.animating,ma.me.me.channeling,ma.me.me.charming,ma.me.me.convoking,ma.me.me.cursing,ma.me.ph.binding,ma.me.ph.brewing,ma.me.ph.chanting,ma.me.ph.dancing,ma.me.ph.enchanting,ma.me.ph.evoking,ma.me.ph.healing,ma.me.ph.scrying,ma.me.sp.abjuring,ma.me.sp.banishing,ma.me.sp.conjuring,ma.me.sp.divining,ma.me.sp.summoning,ma.it.he.wand,ma.it.he.rod,ma.it.he.staff,ma.it.wo.amulet,ma.it.wo.ring,ma.it.talisman,cr.sm.gold,cr.sm.silver,cr.ca.turning,cr.ca.whittling,cr.hus.pl.herbal,cr.pot.fo.shaping,cr.ma.weaving}; + +#nop --- Number of skills we expect (for "all received" check). --- ; +#var {mindspace_skills_needed} {35}; + +#nop --- Shared handler: store bonus for current char, increment done, finish when all received. --- ; +#alias {/mindspace_skill_captured} { + #var {mindspace_skill_old} {$mindspace_data[$GMCP[charinfo][capname]][skills][%1]}; + #var {mindspace_data[$GMCP[charinfo][capname]][skills][%1]} {%2}; + #if {"$mindspace_skill_old" == ""} { + #math {mindspace_skills_done} {$mindspace_skills_done + 1}; + }; + #if {$mindspace_skills_done >= $mindspace_skills_needed} { + /mindspace_skills_finish; + }; +}; + +#nop --- Called when all skills captured: clear flag, persist, notify. --- ; +#alias {/mindspace_skills_finish} { + #var {mindspace_waiting_skills} {0}; + /writemindspacetofile; + #delay {1} {#showme {Skills and mindspace recorded. Check $mindspace_data[$GMCP[charinfo][capname]][skills] for bonuses.<099>}}; +}; + +#nop --- Two actions per skill: table (branched | | name.... level bonus) and raw (path level bonus). --- ; +#nop Table depth: | | | = 3, | | = 2, | | | | = 4. %1=level %2=bonus in both. --- ; + +#action {^| | | air{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured air $mindspace_tmp_bonus }; +}; +#action {^ma.me.el.air%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured air $mindspace_tmp_bonus }; +}; +#action {^| | | earth{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured earth $mindspace_tmp_bonus }; +}; +#action {^ma.me.el.earth%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured earth $mindspace_tmp_bonus }; +}; +#action {^| | | fire{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured fire $mindspace_tmp_bonus }; +}; +#action {^ma.me.el.fire%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured fire $mindspace_tmp_bonus }; +}; +#action {^| | | water{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured water $mindspace_tmp_bonus }; +}; +#action {^ma.me.el.water%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured water $mindspace_tmp_bonus }; +}; +#action {^| | | animating{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured animating $mindspace_tmp_bonus }; +}; +#action {^ma.me.me.animating%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured animating $mindspace_tmp_bonus }; +}; +#action {^| | | channeling{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured channeling $mindspace_tmp_bonus }; +}; +#action {^ma.me.me.channeling%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured channeling $mindspace_tmp_bonus }; +}; +#action {^| | | charming{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured charming $mindspace_tmp_bonus }; +}; +#action {^ma.me.me.charming%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured charming $mindspace_tmp_bonus }; +}; +#action {^| | | convoking{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured convoking $mindspace_tmp_bonus }; +}; +#action {^ma.me.me.convoking%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured convoking $mindspace_tmp_bonus }; +}; +#action {^| | | cursing{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured cursing $mindspace_tmp_bonus }; +}; +#action {^ma.me.me.cursing%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured cursing $mindspace_tmp_bonus }; +}; +#action {^| | | binding{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured binding $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.binding%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured binding $mindspace_tmp_bonus }; +}; +#action {^| | | brewing{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured brewing $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.brewing%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured brewing $mindspace_tmp_bonus }; +}; +#action {^| | | chanting{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured chanting $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.chanting%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured chanting $mindspace_tmp_bonus }; +}; +#action {^| | | dancing{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured dancing $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.dancing%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured dancing $mindspace_tmp_bonus }; +}; +#action {^| | | enchanting{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured enchanting $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.enchanting%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured enchanting $mindspace_tmp_bonus }; +}; +#action {^| | | evoking{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured evoking $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.evoking%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured evoking $mindspace_tmp_bonus }; +}; +#action {^| | | healing{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured healing $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.healing%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured healing $mindspace_tmp_bonus }; +}; +#action {^| | | scrying{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured scrying $mindspace_tmp_bonus }; +}; +#action {^ma.me.ph.scrying%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured scrying $mindspace_tmp_bonus }; +}; +#action {^| | | abjuring{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured abjuring $mindspace_tmp_bonus }; +}; +#action {^ma.me.sp.abjuring%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured abjuring $mindspace_tmp_bonus }; +}; +#action {^| | | banishing{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured banishing $mindspace_tmp_bonus }; +}; +#action {^ma.me.sp.banishing%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured banishing $mindspace_tmp_bonus }; +}; +#action {^| | | conjuring{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured conjuring $mindspace_tmp_bonus }; +}; +#action {^ma.me.sp.conjuring%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured conjuring $mindspace_tmp_bonus }; +}; +#action {^| | | divining{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured divining $mindspace_tmp_bonus }; +}; +#action {^ma.me.sp.divining%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured divining $mindspace_tmp_bonus }; +}; +#action {^| | | summoning{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured summoning $mindspace_tmp_bonus }; +}; +#action {^ma.me.sp.summoning%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured summoning $mindspace_tmp_bonus }; +}; +#action {^| | | wand{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured wand $mindspace_tmp_bonus }; +}; +#action {^ma.it.he.wand%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured wand $mindspace_tmp_bonus }; +}; +#action {^| | | rod{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured rod $mindspace_tmp_bonus }; +}; +#action {^ma.it.he.rod%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured rod $mindspace_tmp_bonus }; +}; +#action {^| | | staff{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured staff $mindspace_tmp_bonus }; +}; +#action {^ma.it.he.staff%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured staff $mindspace_tmp_bonus }; +}; +#action {^| | | amulet{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured amulet $mindspace_tmp_bonus }; +}; +#action {^ma.it.wo.amulet%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured amulet $mindspace_tmp_bonus }; +}; +#action {^| | | ring{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured ring $mindspace_tmp_bonus }; +}; +#action {^ma.it.wo.ring%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured ring $mindspace_tmp_bonus }; +}; +#action {^| | talisman{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured talisman $mindspace_tmp_bonus }; +}; +#action {^ma.it.talisman%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured talisman $mindspace_tmp_bonus }; +}; +#action {^| | | gold{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured gold $mindspace_tmp_bonus }; +}; +#action {^cr.sm.gold%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured gold $mindspace_tmp_bonus }; +}; +#action {^| | | silver{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured silver $mindspace_tmp_bonus }; +}; +#action {^cr.sm.silver%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured silver $mindspace_tmp_bonus }; +}; +#action {^| | | turning{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured turning $mindspace_tmp_bonus }; +}; +#action {^cr.ca.turning%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured turning $mindspace_tmp_bonus }; +}; +#action {^| | | whittling{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured whittling $mindspace_tmp_bonus }; +}; +#action {^cr.ca.whittling%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured whittling $mindspace_tmp_bonus }; +}; +#action {^| | | | herbal{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured herbal $mindspace_tmp_bonus }; +}; +#action {^cr.hus.pl.herbal%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured herbal $mindspace_tmp_bonus }; +}; +#action {^| | | | shaping{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured shaping $mindspace_tmp_bonus }; +}; +#action {^cr.pot.fo.shaping%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured shaping $mindspace_tmp_bonus }; +}; +#action {^| | | weaving{.+} %+1..4d %+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured weaving $mindspace_tmp_bonus }; +}; +#action {^cr.ma.weaving%s%+1..4d%s%+1..4d$} { + #if {"$mindspace_waiting_skills" == "1"} { #var {mindspace_tmp_bonus} {%2}; /mindspace_skill_captured weaving $mindspace_tmp_bonus }; +}; + +#class {mindspace_skills} {close}; diff --git a/src/mindspace/spellbook.tin b/src/mindspace/spellbook.tin new file mode 100644 index 0000000..df8b4fe --- /dev/null +++ b/src/mindspace/spellbook.tin @@ -0,0 +1,77 @@ +#class {spellbook} {kill}; +#class {spellbook} {open}; + +#nop ============================================================================= ; +#nop spellbook.tin - Gained/lost spell tracking (Step 8). Called via /spellbook_do_gained_lost from mindspace. ; +#nop Expects: mindspace_spells (list), mindspace_spell_count (set by mindspace). ; +#nop Uses: spell_data (from spelldata, read by mindspace). ; +#nop ============================================================================= ; + +#nop --- Previous run is in mindspace_data[char][prev] directly, no working copy. --- ; +#list {mindspace_current_shorts} {create}; +#list {mindspace_gained} {create}; +#list {mindspace_lost} {create}; + +#nop --- Build current acronym list, diff vs previous, report gained/lost, save current as previous. --- ; +#alias {/spellbook_do_gained_lost} { + #list {mindspace_current_shorts} {clear}; + #if {$mindspace_spell_count > 0} { + #loop 1 $mindspace_spell_count {idx} { + #var {mindspace_sn} {$mindspace_spells[$idx]}; + #var {mindspace_acr} {$spell_data[$mindspace_sn][short]}; + #list {mindspace_current_shorts} {add} {$mindspace_acr}; + }; + }; + #list {mindspace_current_shorts} {size} {mindspace_cur_size}; + #nop --- Previous for current char is mindspace_data[char][prev]. get size (0 if empty/missing). --- ; + #if {"$mindspace_data[$GMCP[charinfo][capname]][prev]" == ""} { + #var {mindspace_prev_size} {0}; + } { + #list {mindspace_data[$GMCP[charinfo][capname]][prev]} {size} {mindspace_prev_size}; + }; + #if {$mindspace_prev_size == 0} { + #nop Fresh load: no previous data. Save current for next run, no reporting. --- ; + #var {mindspace_data[$GMCP[charinfo][capname]][prev]} {$mindspace_current_shorts}; + #return; + }; + #nop --- Gained: in current but not in previous --- ; + #list {mindspace_gained} {clear}; + #loop 1 $mindspace_cur_size {ci} { + #var {mindspace_acr} {$mindspace_current_shorts[$ci]}; + #var {mindspace_in_prev} {0}; + #loop 1 $mindspace_prev_size {pj} { + #if {"$mindspace_acr" == "$mindspace_data[$GMCP[charinfo][capname]][prev][$pj]"} { #var {mindspace_in_prev} {1} }; + }; + #if {$mindspace_in_prev == 0} { #list {mindspace_gained} {add} {$mindspace_acr} }; + }; + #nop --- Lost: in previous but not in current --- ; + #list {mindspace_lost} {clear}; + #loop 1 $mindspace_prev_size {pi} { + #var {mindspace_acr} {$mindspace_data[$GMCP[charinfo][capname]][prev][$pi]}; + #var {mindspace_in_cur} {0}; + #loop 1 $mindspace_cur_size {cj} { + #if {"$mindspace_acr" == "$mindspace_current_shorts[$cj]"} { #var {mindspace_in_cur} {1} }; + }; + #if {$mindspace_in_cur == 0} { #list {mindspace_lost} {add} {$mindspace_acr} }; + }; + #list {mindspace_gained} {size} {mindspace_gained_count}; + #list {mindspace_lost} {size} {mindspace_lost_count}; + #if {$mindspace_gained_count > 0} { + #var {mindspace_gained_line} {}; + #loop 1 $mindspace_gained_count {gi} { + #if {"$mindspace_gained_line" != ""} { #var {mindspace_gained_line} {$mindspace_gained_line, $mindspace_gained[$gi]} } { #var {mindspace_gained_line} {$mindspace_gained[$gi]} }; + }; + #showme {You have GAINED the following spell(s) since last update: $mindspace_gained_line<099>}; + }; + #if {$mindspace_lost_count > 0} { + #var {mindspace_lost_line} {}; + #loop 1 $mindspace_lost_count {li} { + #if {"$mindspace_lost_line" != ""} { #var {mindspace_lost_line} {$mindspace_lost_line, $mindspace_lost[$li]} } { #var {mindspace_lost_line} {$mindspace_lost[$li]} }; + }; + #showme {<119>You have LOST the following spell(s) since last update: $mindspace_lost_line<099>}; + }; + #nop --- Save current as previous for current char. --- ; + #var {mindspace_data[$GMCP[charinfo][capname]][prev]} {$mindspace_current_shorts}; +}; + +#class {spellbook} {close}; diff --git a/src/mindspace/spellcheck.tin b/src/mindspace/spellcheck.tin new file mode 100644 index 0000000..2bb59f9 --- /dev/null +++ b/src/mindspace/spellcheck.tin @@ -0,0 +1,265 @@ +#class {spellcheck} {kill}; +#class {spellcheck} {open}; + +#nop ============================================================================= +#nop spellcheck.tin - Show spell details + spellcheck table. Read by mindspace.tin. +#nop Expects: mindspace_show_spell_name (set by mindspace before /mindspace_show_spell). +#nop Uses: spell_data, methods_data, books, type_short, skill_levels, order_shorts, skill_tree, mindspace_data, GMCP (from spelldata / mindspace). +#nop Events: PRESSED SECURE LINK MINDSPACEBOOK (book listing), PRESSED SECURE LINK MINDSPACESPELL (show spell). +#nop Colors: xterm 256 format - foreground only. +#nop ============================================================================= + + + +#nop --- Returns [bonus] with color by level: red->orange->yellow->green (reversed greens: ada=90%, afa=99+). --- ; +#function {bonus_colored} { + #if {"%1" == "1"} {#return {[%2<099>]}}; + #if {"%1" == "2"} {#return {[%2<099>]}}; + #if {"%1" == "3"} {#return {[%2<099>]}}; + #if {"%1" == "4"} {#return {[%2<099>]}}; + #if {"%1" == "5"} {#return {[%2<099>]}}; + #if {"%1" == "6"} {#return {[%2<099>]}}; + #if {"%1" == "7"} {#return {[%2<099>]}}; + #if {"%1" == "8"} {#return {[%2<099>]}}; + #if {"%1" == "9"} {#return {[%2<099>]}}; + #if {"%1" == "10"} {#return {[%2<099>]}}; + #return {[%2<099>]}; +}; + +#alias {/mindspace_show_spell} { + #var {mindspace_bid} {$spell_data[$mindspace_show_spell_name][bookid]}; + #format {mindspace_acr} {%u} {$spell_data[$mindspace_show_spell_name][short]}; + #showme {}; + #showme {$mindspace_acr: $mindspace_show_spell_name<099>. Mindspace = $spell_data[$mindspace_show_spell_name][mindspace]<099>, GP Cost = $spell_data[$mindspace_show_spell_name][gp]<099>}; + #showme {It is categorised as $spell_data[$mindspace_show_spell_name][type].<099>}; + #showme {Components: $spell_data[$mindspace_show_spell_name][components]<099>}; + #if {"$spell_data[$mindspace_show_spell_name][hasbook]" == "true"} { + #showme {This is a witch spell.<099>}; + } { + #if {"$mindspace_bid" != ""} { + #showme {It is found in '@book_link{$mindspace_bid;$books[$mindspace_bid][title]}<099>' - '$books[$mindspace_bid][desc]'.<099>}; + #showme {$books[$mindspace_bid][location]<099>}; + } { + #showme {It is unknown where this spell can be obtained.<099>}; + }; + }; + #showme {}; + #showme {(type 'mindspace skills<099>' to update the plugin with your current bonuses for accurate spellchecks)<099>}; + #var {mindspace_char} {$GMCP[charinfo][capname]}; + #var {mindspace_char_order} {0}; + #if {"$order_shorts[$GMCP[charinfo][guildspec]]" != ""} { + #var {mindspace_char_order} {$order_shorts[$GMCP[charinfo][guildspec]]} + } { + #if {"$GMCP[charinfo][guild]" == "wizards"} { + #var {mindspace_char_order} {3} + }; + }; + #showme {Skill Fail ->->->->->->->->->->-> Equal ->->->->->->->->->->->- Success Chance [bonus]<099>}; + #foreach {*methods_data[$mindspace_show_spell_name][%*]} {mindspace_m} { + #var {mindspace_bonus} {$mindspace_data[$mindspace_char][skills][$mindspace_m]}; + #if {"$mindspace_bonus" == ""} { + #var {mindspace_bonus} {0} + }; + #var {mindspace_level} {1}; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][1]} { + #var {mindspace_level} {2} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][2]} { + #var {mindspace_level} {3} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][3]} { + #var {mindspace_level} {4} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][4]} { + #var {mindspace_level} {5} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][5]} { + #var {mindspace_level} {6} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][6]} { + #var {mindspace_level} {7} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][7]} { + #var {mindspace_level} {8} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][8]} { + #var {mindspace_level} {9} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][9]} { + #var {mindspace_level} {10} + }; + #if {$mindspace_bonus >= $methods_data[$mindspace_show_spell_name][$mindspace_m][10]} { + #var {mindspace_level} {11} + }; + #var {mindspace_pre} {}; + #var {mindspace_mid} {}; + #var {mindspace_post} {}; + #format {mindspace_cell} {\x3c%s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][1]}; + #format {mindspace_cell} {%-9s} {$mindspace_cell}; + #if {1 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {1 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][2]}; + #if {2 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {2 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][2]}; + #if {3 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {3 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][3]}; + #if {4 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {4 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][4]}; + #if {5 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {5 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][5]}; + #if {6 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {6 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][6]}; + #if {7 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {7 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][7]}; + #if {8 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {8 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][8]}; + #if {9 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {9 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][9]}; + #if {10 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {10 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_cell} {%-6s} {$methods_data[$mindspace_show_spell_name][$mindspace_m][10]}; + #if {11 < $mindspace_level} { + #var {mindspace_pre} {$mindspace_pre$mindspace_cell} + } { + #if {11 == $mindspace_level} { + #var {mindspace_mid} {$mindspace_mid$mindspace_cell} + } { + #var {mindspace_post} {$mindspace_post$mindspace_cell} + } + }; + #format {mindspace_skill_fmt} {%-12s } {$mindspace_m}; + #var {mindspace_p_suffix} {}; + #if {$mindspace_char_order > 0 && "$skill_tree[$mindspace_m][orders]" != ""} { + #foreach {*skill_tree[$mindspace_m][orders][%*]} {_oidx} { + #if {"$skill_tree[$mindspace_m][orders][$_oidx]" == "$mindspace_char_order"} { + #var {mindspace_p_suffix} { *P*<099>}; + #break + }; + }; + }; + #showme {$mindspace_skill_fmt$mindspace_pre$mindspace_mid$mindspace_post<099> $skill_levels[$mindspace_level]@bonus_colored{$mindspace_level;$mindspace_bonus}<099>$mindspace_p_suffix}; + }; + #showme {}; +}; + +#nop --- MSLP SECURE LINK: book name -> clickable, passes book ID, outputs Quow-style book listing --- ; +#event {PRESSED SECURE LINK MSBOOK MOUSE BUTTON ONE} { + #showme {clicked book link ><><}; + #var {mindspace_bid} {%4}; + #if {"$books[$mindspace_bid][title]" == ""} { + #showme {<118>Unknown book ID.<099>}; + #return; + }; + #showme {}; + #showme {Listing spells found in book '$books[$mindspace_bid][title]'<099>}; + #showme {($books[$mindspace_bid][location]):<099>}; + #showme {}; + #var {mindspace_book_idx} {0}; + #foreach {*spell_data[%*]} {mindspace_sn} { + #if {"$spell_data[$mindspace_sn][bookid]" == "$mindspace_bid"} { + #math {mindspace_book_idx} {$mindspace_book_idx + 1}; + #var {mindspace_ts} {$type_short[$spell_data[$mindspace_sn][type]]}; + #format {mindspace_book_fmt} {%-2s} {$mindspace_book_idx}; + #format {mindspace_acr} {%u} {$spell_data[$mindspace_sn][short]}; + #showme { $mindspace_book_fmt: <099>@spell_link{$mindspace_sn;$mindspace_acr} - $mindspace_sn ($mindspace_ts) [size:$spell_data[$mindspace_sn][mindspace], GP:$spell_data[$mindspace_sn][gp]]<099>}; + }; + }; + #showme {}; +}; + +#nop --- MSLP SECURE LINK: spell acronym/name -> clickable, triggers show spell --- ; +#event {PRESSED SECURE LINK MINDSPACESPELL MOUSE BUTTON ONE} { + #var {mindspace_show_spell_name} {%4}; + /mindspace_show_spell; +}; + +#function {spell_link} { + #nop %1 = full spell name, %2 = display text (e.g. acronym); + #return {\e]68;2;MINDSPACESPELL;%1\a\e[4m%2\e[24m}; +} + +#function {book_link} { + #showme {created book link ><><}; + #nop %1 = book id, %2 = display text; + #return {\e]68;2;MSBOOK;%1\a\e[4m%2\e[24m}; +} + +#class {spellcheck} {close}; diff --git a/src/mindspace/spelldata.tin b/src/mindspace/spelldata.tin new file mode 100644 index 0000000..5f3dda9 --- /dev/null +++ b/src/mindspace/spelldata.tin @@ -0,0 +1,1581 @@ +#class {spelldata} {kill}; +#class {spelldata} {open}; + +#nop ============================================================================= ; +#nop spelldata.tin - Static spell/skill/book/order data from Quow's Mindspace plugin ; +#nop Converted for TinTin++. Use #read spelldata.tin to load. ; +#nop ============================================================================= ; + +#nop --- sQuowSkillLevels: 1-based index -> chance string --- ; +#var {skill_levels} { + {1} {<1% chance } + {2} {10% chance } + {3} {20% chance } + {4} {30% chance } + {5} {40% chance } + {6} {50% chance } + {7} {60% chance } + {8} {70% chance } + {9} {80% chance } + {10} {90% chance } + {11} {99+% chance } +}; + +#nop --- sQuowBooks: Quow's book id -> title, desc, location, libnum (when UU Library) --- ; +#var {books} { + {1} { + {title} {Advanced Arts and Sciences of the Cunning Artificer} + {desc} {filigreed hardcover} + {location} {Bes Pelargic Library: random spot of the 4 book locations} + } + {2} { + {title} {Ae Thouesand and Onne Thyngs ae Boye cann Do with Corpse Partes} + {desc} {skin-bound grimoire} + {location} {UU Library: [Book 9] @ 55 Thaums (row 11, column 5)} + {libnum} {9} + } + {3} { + {title} {Begynners' Magick} + {desc} {tome} + {location} {Any Gym practise room} + } + {4} { + {title} {Body Parts for the Advanced Student} + {desc} {bone book} + {location} {UU Library: [Book 14] @ 110 Thaums (row 22, column 6)} + {libnum} {14} + } + {5} { + {title} {Cunjuryng for Begynners} + {desc} {nice and shiny book} + {location} {UU Library: [Book 7] @ 35 Thaums (row 7, column 1)} + {libnum} {7} + } + {6} { + {title} {Divinational Arts} + {desc} {origami tome} + {location} {Bes Pelargic Library: random spot of the 4 book locations} + } + {7} { + {title} {Everything You Ever Wanted to Know About Everything} + {desc} {mysterious book} + {location} {UU Library: [Book 10] @ 75 Thaums (row 15, column 3)} + {libnum} {10} + } + {8} { + {title} {Heavenly Arts} + {desc} {gemmed volume} + {location} {Bes Pelargic Library: random spot of the 4 book locations} + } + {9} { + {title} {Illusionary Arts} + {desc} {shimmering grimoire} + {location} {Bes Pelargic Library: random spot of the 4 book locations} + } + {10} { + {title} {Intermediate Artifact Magic, fourth ed.} + {desc} {solid book} + {location} {UU Library: [Book 13] @ 95 Thaums (row 19, column 1)} + {libnum} {13} + } + {11} { + {title} {Introeductyon too thee Strukture of thee Multyverss} + {desc} {well-read volume} + {location} {UU Library: [Book 8] @ 50 Thaums (row 10, column 8)} + {libnum} {8} + } + {12} { + {title} {Invoakatyons in thee Mysterie of Wynd} + {desc} {rather torn volume} + {location} {UU Library: [Book 3] @ 20 Thaums (row 4, column 2)} + {libnum} {3} + } + {13} { + {title} {My Firste Demon-Summoning Booke! By Beatrix Faust} + {desc} {cheerful yellow book} + {location} {UU Library: [Book 15] @ 110 Thaums (row 22, column 8)} + {libnum} {15} + } + {14} { + {title} {Spelles of thee Arte of Fyre} + {desc} {slightly charred grimoire} + {location} {UU Library: [Book 4] @ 25 Thaums (row 5, column 7)} + {libnum} {4} + } + {15} { + {title} {Spelles of thee Oryent} + {desc} {date encrusted grimoire} + {location} {IIL Library, DJB: Back-right/East} + } + {16} { + {title} {The Olde Arte of Foode} + {desc} {sticky grimoire} + {location} {UU Library: [Book 11] @ 80 Thaums (row 16, column 4)} + {libnum} {11} + } + {17} { + {title} {Thee Dyscyplyne of thee Stoane} + {desc} {weighty tome} + {location} {UU Library: [Book 6] @ 30 Thaums (row 6, column 6)} + {libnum} {6} + } + {18} { + {title} {Thee Secrets of Artifyce and Crafte} + {desc} {imposing bronze book} + {location} {AoA Library, Sto Lat: second aisle, first aisle, first aisle} + } + {19} { + {title} {Weather Magick} + {desc} {impressive grimoire} + {location} {UU Library: [Book 12] @ 85 Thaums (row 17, column 7)} + {libnum} {12} + } + {20} { + {title} {Ye Booke of Polymorphe} + {desc} {\[book appearance changes\]} + {location} {IIL Library, DJB: Back-left/West} + } + {21} { + {title} {Spells of Thee Moste Ceremonious Rite of AshkEnte} + {desc} {plain black book} + {location} {UU Library: [Book 16] @ 115 Thaums (row 23, column 3)} + {libnum} {16} + } + {22} { + {title} {Jorodin} + {desc} {Jorodin} + {location} {Jorodin's Shop, Short Street, AM} + } + {23} { + {title} {Pracktical Hedgecrafte} + {desc} {bark-bound book} + {location} {Hedge Outpost, Skund Forest} + } +}; + +#nop --- sQuowOrders: order id 1-8 -> name, theme --- ; +#var {orders} { + {1} { + {name} {Circle / White } + {theme} {Elementalists} + } + {2} { + {name} {Hoodwinkers / Blue } + {theme} {Illusions} + } + {3} { + {name} {Lodgers / Red } + {theme} {Thaumaturgy} + } + {4} { + {name} {Silver Star / Brown } + {theme} {Magical Artifacts} + } + {5} { + {name} {Seers / Green } + {theme} {Lore and Knowledge} + } + {6} { + {name} {Shadow / Black } + {theme} {Demonology} + } + {7} { + {name} {Midnight / Grey } + {theme} {Necromancy} + } + {8} { + {name} {Last Order / Octarine } + {theme} {Octarine Killers} + } +}; + +#nop --- sOrderShorts: user input -> order number --- ; +#var {order_shorts} { + {circle} {1} + {white} {1} + {hoodwinkers} {2} + {blue} {2} + {lodgers} {3} + {red} {3} + {silver} {4} + {star} {4} + {silver star} {4} + {silverstar} {4} + {brown} {4} + {seer} {5} + {seers} {5} + {green} {5} + {shadow} {6} + {shadows} {6} + {black} {6} + {midnight} {7} + {grey} {7} + {last} {8} + {lastorder} {8} + {pk} {8} + {last order} {8} + {octarine} {8} +}; + +#nop --- sQuowSkillTree: skill name -> path, orders (1-indexed list: {1}{orderId}{2}{orderId}... empty = no primaries) --- ; +#var {skill_tree} { + {air} { + {path} {ma.me.el.air} + {orders} {{1}{1}{2}{2}} + } + {earth} { + {path} {ma.me.el.earth} + {orders} {{1}{1}} + } + {fire} { + {path} {ma.me.el.fire} + {orders} {{1}{1}{2}{6}{3}{8}} + } + {water} { + {path} {ma.me.el.water} + {orders} {{1}{1}} + } + {animating} { + {path} {ma.me.me.animating} + {orders} {{1}{2}{2}{3}{3}{5}{4}{7}{5}{8}} + } + {channeling} { + {path} {ma.me.me.channeling} + {orders} {{1}{1}{2}{2}{3}{3}{4}{5}{5}{8}} + } + {charming} { + {path} {ma.me.me.charming} + {orders} {{1}{2}{2}{6}{3}{7}} + } + {convoking} { + {path} {ma.me.me.convoking} + {orders} {{1}{1}{2}{2}{3}{3}{4}{5}{5}{7}{6}{8}} + } + {cursing} { + {path} {ma.me.me.cursing} + {orders} {{1}{6}} + } + {binding} { + {path} {ma.me.ph.binding} + {orders} {{1}{1}{2}{2}{3}{3}{4}{4}{5}{5}{6}{6}{7}{7}{8}{8}} + } + {brewing} { + {path} {ma.me.ph.brewing} + {orders} {} + } + {chanting} { + {path} {ma.me.ph.chanting} + {orders} {{1}{6}} + } + {dancing} { + {path} {ma.me.ph.dancing} + {orders} {{1}{3}{2}{6}} + } + {enchanting} { + {path} {ma.me.ph.enchanting} + {orders} {{1}{3}{2}{4}{3}{5}{4}{7}} + } + {evoking} { + {path} {ma.me.ph.evoking} + {orders} {{1}{1}{2}{2}{3}{3}{4}{5}{5}{8}} + } + {healing} { + {path} {ma.me.ph.healing} + {orders} {{1}{7}} + } + {scrying} { + {path} {ma.me.ph.scrying} + {orders} {{1}{5}{2}{8}} + } + {abjuring} { + {path} {ma.me.sp.abjuring} + {orders} {{1}{6}{2}{7}} + } + {banishing} { + {path} {ma.me.sp.banishing} + {orders} {{1}{6}} + } + {conjuring} { + {path} {ma.me.sp.conjuring} + {orders} {{1}{2}} + } + {divining} { + {path} {ma.me.sp.divining} + {orders} {{1}{5}} + } + {summoning} { + {path} {ma.me.sp.summoning} + {orders} {{1}{1}{2}{2}{3}{3}{4}{6}{5}{7}{6}{8}} + } + {wand} { + {path} {ma.it.he.wand} + {orders} {{1}{4}} + } + {rod} { + {path} {ma.it.he.rod} + {orders} {{1}{4}} + } + {staff} { + {path} {ma.it.he.staff} + {orders} {{1}{1}{2}{2}{3}{3}{4}{4}{5}{5}{6}{6}{7}{7}{8}{8}} + } + {amulet} { + {path} {ma.it.wo.amulet} + {orders} {} + } + {ring} { + {path} {ma.it.wo.ring} + {orders} {{1}{4}} + } + {talisman} { + {path} {ma.it.talisman} + {orders} {} + } + {gold} { + {path} {cr.sm.gold} + {orders} {{1}{4}} + } + {silver} { + {path} {cr.sm.silver} + {orders} {} + } + {turning} { + {path} {cr.ca.turning} + {orders} {{1}{4}} + } + {whittling} { + {path} {cr.ca.whittling} + {orders} {{1}{4}} + } + {herbal} { + {path} {cr.hus.pl.herbal} + {orders} {} + } + {shaping} { + {path} {cr.pot.fo.shaping} + {orders} {} + } + {weaving} { + {path} {cr.ma.weaving} + {orders} {} + } +}; + +#nop --- iQuowSkillOrder: index 1-35 -> skill name --- ; +#var {skill_order} { + {1} {air} + {2} {earth} + {3} {fire} + {4} {water} + {5} {animating} + {6} {channeling} + {7} {charming} + {8} {convoking} + {9} {cursing} + {10} {binding} + {11} {brewing} + {12} {chanting} + {13} {dancing} + {14} {enchanting} + {15} {evoking} + {16} {healing} + {17} {scrying} + {18} {abjuring} + {19} {banishing} + {20} {conjuring} + {21} {divining} + {22} {summoning} + {23} {wand} + {24} {rod} + {25} {staff} + {26} {amulet} + {27} {ring} + {28} {talisman} + {29} {gold} + {30} {silver} + {31} {turning} + {32} {whittling} + {33} {herbal} + {34} {shaping} + {35} {weaving} +}; + +#nop --- sTypeShort: category name -> short code --- ; +#var {type_short} { + {Defensive} {def} + {Offensive} {off} + {Miscellaneous} {misc} +}; + +#nop --- objQuowSpellData: spell name -> mindspace, short, type, gp, bookid, hasbook, components. Methods in methods_data. --- ; +#var {spell_data} { + {A Cup of Tea and Sake} { + {mindspace}{5} + {short}{cts} + {type}{Miscellaneous} + {gp}{25} + {bookid}{6} + {hasbook}{false} + {components}{paper (consumed)} + } + {Al'Hrahaz's Scintillating Blorpler} { + {mindspace}{25} + {short}{ahsb} + {type}{Miscellaneous} + {gp}{90} + {bookid}{15} + {hasbook}{false} + {components}{octogram, wearable jewellery (to blorp)} + } + {Amazing Silicate Blorpler} { + {mindspace}{5} + {short}{asb} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{rock (to blorp)} + } + {Atmospheric Inscription Wonder} { + {mindspace}{25} + {short}{aiw} + {type}{Miscellaneous} + {gp}{80} + {bookid}{14} + {hasbook}{false} + {components}{quill (consumed), torch (consumed)} + } + {Bifram's Amazing Fireworks} { + {mindspace}{15} + {short}{baf} + {type}{Miscellaneous} + {gp}{50} + {bookid}{14} + {hasbook}{false} + {components}{torch (consumed)} + } + {Booch's Extremal Polymorphism} { + {mindspace}{50} + {short}{bep} + {type}{Miscellaneous} + {gp}{70} + {bookid}{20} + {hasbook}{false} + {components}{dwarf arm, human arm, troll arm (all consumed)} + } + {Boolywog's Forbidden Pleasures} { + {mindspace}{10} + {short}{bfp} + {type}{Miscellaneous} + {gp}{25} + {bookid}{3} + {hasbook}{false} + {components}{octogram, potato (consumed)} + } + {Brassica Oleracea Ambulata} { + {mindspace}{10} + {short}{boa} + {type}{Miscellaneous} + {gp}{25} + {bookid}{5} + {hasbook}{false} + {components}{none} + } + {Brother Happalon's Elementary Enchanting} { + {mindspace}{5} + {short}{bhee} + {type}{Miscellaneous} + {gp}{35} + {bookid}{11} + {hasbook}{false} + {components}{blue funnel (sometimes consumed)} + } + {Calm Embrace of Illusionary Beauty} { + {mindspace}{25} + {short}{ceib} + {type}{Offensive} + {gp}{50} + {bookid}{9} + {hasbook}{false} + {components}{staff, fire cracker or firecracker (consumed)} + } + {Cherry Blossoms in Bloom} { + {mindspace}{10} + {short}{cbb} + {type}{Miscellaneous} + {gp}{25} + {bookid}{9} + {hasbook}{false} + {components}{octogram} + } + {Chrenedict's Corporeal Covering} { + {mindspace}{25} + {short}{ccc} + {type}{Defensive} + {gp}{60} + {bookid}{17} + {hasbook}{false} + {components}{sap, varnish, agent [chalk, latex, filings] - all consumed} + } + {Collatrap's Instant Pickling Stick} { + {mindspace}{10} + {short}{cips} + {type}{Miscellaneous} + {gp}{20} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Crondor's Fabulous Detection} { + {mindspace}{10} + {short}{cfd} + {type}{Miscellaneous} + {gp}{25} + {bookid}{10} + {hasbook}{false} + {components}{gold ring, staff} + } + {Crondor's Marvellous Sequestration} { + {mindspace}{60} + {short}{cms2} + {type}{Miscellaneous} + {gp}{350} + {bookid}{18} + {hasbook}{false} + {components}{octogram, sapphire ring (consumed), handful of pmp (consumed)} + } + {Crondor's Mysterious Sparkling} { + {mindspace}{40} + {short}{cms} + {type}{Miscellaneous} + {gp}{150} + {bookid}{10} + {hasbook}{false} + {components}{octogram, ankhstone (consumed)} + } + {Dismal Digit of Doom} { + {mindspace}{10} + {short}{ddd} + {type}{Miscellaneous} + {gp}{30} + {bookid}{2} + {hasbook}{false} + {components}{index finger (consumed), pinch of chalk powder (consumed)} + } + {Doctor Kelleflump's Deadly Demon} { + {mindspace}{60} + {short}{dkdd} + {type}{Offensive} + {gp}{55} + {bookid}{13} + {hasbook}{false} + {components}{human heart (consumed)} + } + {Doctor Worblehat's Flaming Primate Premonition} { + {mindspace}{25} + {short}{dwfpp} + {type}{Miscellaneous} + {gp}{100} + {bookid}{18} + {hasbook}{false} + {components}{octogram, knife, small rowan orb (consumed), red crystal (consumed), gold ring (consumed)} + } + {Duander's Thaumic Luminosity Disperser} { + {mindspace}{20} + {short}{dtld} + {type}{Miscellaneous} + {gp}{30} + {bookid}{14} + {hasbook}{false} + {components}{none} + } + {Effermhor's Hypersonic Assault} { + {mindspace}{30} + {short}{eha} + {type}{Offensive} + {gp}{40} + {bookid}{12} + {hasbook}{false} + {components}{jar of screaming mandrake roots, beeswax candle (consumed)} + } + {Ellamandyr's Hyaline Amulet} { + {mindspace}{10} + {short}{eha2} + {type}{Miscellaneous} + {gp}{15} + {bookid}{18} + {hasbook}{false} + {components}{wand, silver necklace (consumed), water (minimum 2 pints)} + } + {Endorphin's Floating Friend} { + {mindspace}{20} + {short}{eff} + {type}{Defensive} + {gp}{30} + {bookid}{11} + {hasbook}{false} + {components}{shield (to float)} + } + {Eringyas' Surprising Bouquet} { + {mindspace}{10} + {short}{esb} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Fabrication Classification Identification} { + {mindspace}{15} + {short}{fci} + {type}{Miscellaneous} + {gp}{45} + {bookid}{11} + {hasbook}{false} + {components}{none} + } + {Fiddelmaker's Auriferous Embrace} { + {mindspace}{20} + {short}{fae} + {type}{Offensive} + {gp}{50} + {bookid}{18} + {hasbook}{false} + {components}{wand, gold ring (consumed)} + } + {Finneblaugh's Thaumic Float} { + {mindspace}{20} + {short}{ftf} + {type}{Miscellaneous} + {gp}{40} + {bookid}{12} + {hasbook}{false} + {components}{feather (consumed)} + } + {Floron's Fabulous Mirror} { + {mindspace}{15} + {short}{ffm} + {type}{Miscellaneous} + {gp}{40} + {bookid}{7} + {hasbook}{false} + {components}{mirror} + } + {Friddlefrod's Hydratic Extrusion} { + {mindspace}{10} + {short}{fhe} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Frygellhan's Fiendish Orbit Disruptor} { + {mindspace}{15} + {short}{ffod} + {type}{Offensive} + {gp}{25} + {bookid}{11} + {hasbook}{false} + {components}{iron chain} + } + {Fyodor's Nimbus of Porterage} { + {mindspace}{35} + {short}{fnp} + {type}{Miscellaneous} + {gp}{75} + {bookid}{12} + {hasbook}{false} + {components}{candle, wet towel (gets damaged)} + } + {G'flott's Olfactory Nightmare} { + {mindspace}{30} + {short}{gon} + {type}{Offensive} + {gp}{40} + {bookid}{12} + {hasbook}{false} + {components}{block of sulphur (consumed)} + } + {Gillimer's Ring of Temperate Weather} { + {mindspace}{25} + {short}{grtw} + {type}{Miscellaneous} + {gp}{100} + {bookid}{19} + {hasbook}{false} + {components}{gold ring (consumed)} + } + {Grisald's Chilly Touch} { + {mindspace}{5} + {short}{gct} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Grisald's Reanimated Guardian} { + {mindspace}{40} + {short}{grg} + {type}{Defensive} + {gp}{70} + {bookid}{4} + {hasbook}{false} + {components}{fully intact human corpse (consumed)} + } + {Gryntard's Feathery Reliever} { + {mindspace}{25} + {short}{gfr} + {type}{Miscellaneous} + {gp}{60} + {bookid}{12} + {hasbook}{false} + {components}{feather (consumed), silver necklace} + } + {Heezlewurst's Elemental Buffer} { + {mindspace}{30} + {short}{heb} + {type}{Defensive} + {gp}{50} + {bookid}{11} + {hasbook}{false} + {components}{octogram (consumed), small rhokshial crystal (consumed - can use multiple crystals)} + } + {Independent Recurring Vocaliser} { + {mindspace}{5} + {short}{irv} + {type}{Miscellaneous} + {gp}{15} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Jogloran's Portal of Cheaper Travel} { + {mindspace}{20} + {short}{jpct} + {type}{Miscellaneous} + {gp}{100} + {bookid}{11} + {hasbook}{false} + {components}{blorped jewellery or rock} + } + {Jorodin's Magnificent Communicator} { + {mindspace}{20} + {short}{jmc} + {type}{Miscellaneous} + {gp}{50} + {bookid}{22} + {hasbook}{false} + {components}{none} + } + {Journey of the Heavenly Storm Dragon} { + {mindspace}{75} + {short}{jhsd} + {type}{Offensive} + {gp}{100} + {bookid}{8} + {hasbook}{false} + {components}{torch (consumed), staff} + } + {Kamikaze Oryctolagus Flammula} { + {mindspace}{30} + {short}{kof} + {type}{Offensive} + {gp}{50} + {bookid}{14} + {hasbook}{false} + {components}{carrot (consumed), torch} + } + {Kelleflump's Irritating Demon} { + {mindspace}{20} + {short}{kid} + {type}{Offensive} + {gp}{30} + {bookid}{13} + {hasbook}{false} + {components}{knife} + } + {Kipperwald's Perlustration Prevention} { + {mindspace}{15} + {short}{kpp} + {type}{Defensive} + {gp}{65} + {bookid}{7} + {hasbook}{false} + {components}{octogram, eye (consumed)} + } + {Malich's AshkEnte Circle} { + {mindspace}{10} + {short}{mac} + {type}{Miscellaneous} + {gp}{20} + {bookid}{21} + {hasbook}{false} + {components}{staff (not consumed), phoenix egg, ram skull, candlestick, beeswax candle (all consumed)} + } + {Malich's AshkEnte Summoning Incantation} { + {mindspace}{60} + {short}{masi} + {type}{Miscellaneous} + {gp}{120} + {bookid}{21} + {hasbook}{false} + {components}{charged ashkente circle} + } + {Malich's Penetrating Ocular Lance} { + {mindspace}{5} + {short}{mpol} + {type}{Offensive} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Master Glimer's Amazing Glowing Thing} { + {mindspace}{30} + {short}{mgagt} + {type}{Miscellaneous} + {gp}{150} + {bookid}{11} + {hasbook}{false} + {components}{octogram, blue funnel (sometimes consumed), handful of pmp (consumed)} + } + {Master Woddeley's Luminescent Companion} { + {mindspace}{10} + {short}{mwlc} + {type}{Miscellaneous} + {gp}{15} + {bookid}{5} + {hasbook}{false} + {components}{none} + } + {Memories of a Vicious Chicken} { + {mindspace}{5} + {short}{mvc} + {type}{Offensive} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{chicken feather (target receives the feather)} + } + {Mugwuddle's Muddling Mirage} { + {mindspace}{25} + {short}{mmm} + {type}{Offensive} + {gp}{55} + {bookid}{5} + {hasbook}{false} + {components}{pinch of tobacco (consumed), mirror, pipe} + } + {Myrandil's Mask of Death} { + {mindspace}{10} + {short}{mmd} + {type}{Miscellaneous} + {gp}{30} + {bookid}{2} + {hasbook}{false} + {components}{octogram, human head (consumed)} + } + {Myrandil's Vicious Seizure} { + {mindspace}{30} + {short}{mvs} + {type}{Offensive} + {gp}{75} + {bookid}{4} + {hasbook}{false} + {components}{hand (consumed)} + } + {Nargl'frob's Empyrean Spear} { + {mindspace}{45} + {short}{nes} + {type}{Offensive} + {gp}{60} + {bookid}{19} + {hasbook}{false} + {components}{torch (consumed)} + } + {Narquin's Hand of Acquisition} { + {mindspace}{30} + {short}{nha} + {type}{Miscellaneous} + {gp}{40} + {bookid}{2} + {hasbook}{false} + {components}{hand (consumed)} + } + {Narquin's Mist of Doom} { + {mindspace}{15} + {short}{nmd} + {type}{Offensive} + {gp}{30} + {bookid}{4} + {hasbook}{false} + {components}{knife} + } + {Objandeller's Thaumic Funnel} { + {mindspace}{15} + {short}{otf} + {type}{Miscellaneous} + {gp}{15} + {bookid}{5} + {hasbook}{false} + {components}{none} + } + {Old Bellicus' Brazen Knuckles} { + {mindspace}{25} + {short}{obbk} + {type}{Offensive} + {gp}{50} + {bookid}{17} + {hasbook}{false} + {components}{brass ring (consumed)} + } + {Patient Taming of the Quantum Weather Butterfly} { + {mindspace}{50} + {short}{ptqwb} + {type}{Miscellaneous} + {gp}{85} + {bookid}{8} + {hasbook}{false} + {components}{octogram} + } + {Polliwiggle's Puissancy Probe} { + {mindspace}{10} + {short}{ppp} + {type}{Miscellaneous} + {gp}{15} + {bookid}{7} + {hasbook}{false} + {components}{none} + } + {Pragi's Fiery Gaze} { + {mindspace}{30} + {short}{pfg} + {type}{Offensive} + {gp}{40} + {bookid}{2} + {hasbook}{false} + {components}{eye (consumed)} + } + {Pragi's Lost Gaze} { + {mindspace}{30} + {short}{plg} + {type}{Offensive} + {gp}{55} + {bookid}{15} + {hasbook}{false} + {components}{hand, left leg, right leg, eye (all consumed)} + } + {Pragi's Molten Gaze} { + {mindspace}{10} + {short}{pmg} + {type}{Miscellaneous} + {gp}{20} + {bookid}{3} + {hasbook}{false} + {components}{shimmering glass nugget} + } + {Professor Flambardie's Grim Amulet} { + {mindspace}{50} + {short}{pfga} + {type}{Miscellaneous} + {gp}{200} + {bookid}{13} + {hasbook}{false} + {components}{wand, silver medallion (consumed), small green crystal (consumed)} + } + {Reckless Encouragement of Arcane Peacock} { + {mindspace}{20} + {short}{reap} + {type}{Offensive} + {gp}{65} + {bookid}{6} + {hasbook}{false} + {components}{tigerwood staff} + } + {Rubayak's Power Dispenser} { + {mindspace}{5} + {short}{rpd} + {type}{Miscellaneous} + {gp}{20} + {bookid}{18} + {hasbook}{false} + {components}{wooden monkey, small red crystal (consumed), banana (consumed)} + } + {Rubayak's Power Storage} { + {mindspace}{30} + {short}{rps} + {type}{Miscellaneous} + {gp}{40} + {bookid}{11} + {hasbook}{false} + {components}{black shell} + } + {Rugged Victor's Rodentia Vivisection} { + {mindspace}{10} + {short}{rvrv} + {type}{Offensive} + {gp}{15} + {bookid}{3} + {hasbook}{false} + {components}{rat tail} + } + {Sageroff's Sentry Summoning} { + {mindspace}{30} + {short}{sss2} + {type}{Defensive} + {gp}{35} + {bookid}{13} + {hasbook}{false} + {components}{octogram, candle (lit), knife, mirror, apple (consumed)} + } + {Scolorid's Scintillating Scribbling} { + {mindspace}{10} + {short}{sss} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Skeetbraskin's Fuliginous Perdition} { + {mindspace}{20} + {short}{sfp2} + {type}{Offensive} + {gp}{20} + {bookid}{13} + {hasbook}{false} + {components}{none} + } + {Sorklin's Field of Protection} { + {mindspace}{10} + {short}{sfp} + {type}{Defensive} + {gp}{15} + {bookid}{3} + {hasbook}{false} + {components}{shield} + } + {Sorsalsean's Seismic Eruption} { + {mindspace}{40} + {short}{sse} + {type}{Offensive} + {gp}{50} + {bookid}{17} + {hasbook}{false} + {components}{pebble (consumed)} + } + {Stacklady's Morphic Resonator} { + {mindspace}{40} + {short}{smr} + {type}{Offensive} + {gp}{60} + {bookid}{20} + {hasbook}{false} + {components}{pinch of powdered frog, pinch of pmp (all consumed)} + } + {Thousand Dancing Celestial Fates} { + {mindspace}{20} + {short}{tdcf} + {type}{Miscellaneous} + {gp}{50} + {bookid}{1} + {hasbook}{false} + {components}{octogram, staff, octiron sliver (consumed)} + } + {Torqvald's Illusion Generatrix} { + {mindspace}{50} + {short}{tig} + {type}{Miscellaneous} + {gp}{100} + {bookid}{5} + {hasbook}{false} + {components}{small bucket, quill (consumed), book end (consumed), silver ring (consumed)} + } + {Torqvald's Many Colours} { + {mindspace}{10} + {short}{tmc} + {type}{Miscellaneous} + {gp}{15} + {bookid}{3} + {hasbook}{false} + {components}{none} + } + {Transcendent Pneumatic Alleviator} { + {mindspace}{25} + {short}{tpa} + {type}{Defensive} + {gp}{45} + {bookid}{12} + {hasbook}{false} + {components}{shield, handful of fine ash (consumed) - grey, black or white ash} + } + {Turnwhistle's Effulgent Autiridescence} { + {mindspace}{10} + {short}{tea} + {type}{Miscellaneous} + {gp}{150} + {bookid}{10} + {hasbook}{false} + {components}{octogram, shimmering glass nugget, lantern (consumed)} + } + {Union of the Phoenix and Divine Dragon} { + {mindspace}{25} + {short}{updd} + {type}{Miscellaneous} + {gp}{50} + {bookid}{9} + {hasbook}{false} + {components}{shimmering glass nugget} + } + {Von Hasselhoff's Skin Condition} { + {mindspace}{35} + {short}{vhsc} + {type}{Offensive} + {gp}{65} + {bookid}{4} + {hasbook}{false} + {components}{human skin (consumed) - can use tanner's kit to divide skins first} + } + {Wonker's Wicked Wobble} { + {mindspace}{45} + {short}{www} + {type}{Offensive} + {gp}{40} + {bookid}{10} + {hasbook}{false} + {components}{rubber knife} + } + {Worstler's Advanced Metallurgical Glance} { + {mindspace}{15} + {short}{wamg} + {type}{Miscellaneous} + {gp}{20} + {bookid}{7} + {hasbook}{false} + {components}{blorped jewellery} + } + {Worstler's Elementary Mineralogical Glance} { + {mindspace}{5} + {short}{wemg} + {type}{Miscellaneous} + {gp}{10} + {bookid}{3} + {hasbook}{false} + {components}{blorped rock} + } + {Wungle's Body Part Suggestion} { + {mindspace}{30} + {short}{wbps} + {type}{Offensive} + {gp}{25} + {bookid}{4} + {hasbook}{false} + {components}{knife (consumed)} + } + {Wungle's Great Sucking} { + {mindspace}{35} + {short}{wgs} + {type}{Offensive} + {gp}{50} + {bookid}{4} + {hasbook}{false} + {components}{human heart (consumed)} + } + {Wurphle's Midnight Snack} { + {mindspace}{25} + {short}{wms} + {type}{Miscellaneous} + {gp}{35} + {bookid}{16} + {hasbook}{false} + {components}{none} + } + {Wurphle's Packed Lunch} { + {mindspace}{10} + {short}{wpl} + {type}{Miscellaneous} + {gp}{300} + {bookid}{16} + {hasbook}{false} + {components}{octogram, sapient pearwood seed, hubland bear fur, wood riving, large rhokshial crystal, threaded needle, knife (only knife and needle not consumed)} + } + {Yordon's Extremal Extension} { + {mindspace}{35} + {short}{yee} + {type}{Miscellaneous} + {gp}{70} + {bookid}{20} + {hasbook}{false} + {components}{dwarf arm, human arm, troll arm (all consumed), octogram} + } + {Banishing of Loquacious Spirits} { + {mindspace}{5} + {short}{bls} + {type}{Miscellaneous} + {gp}{20} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Banishing of Prying Eyes} { + {mindspace}{5} + {short}{bpe} + {type}{Defensive} + {gp}{25} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Banishing of Unnatural Urges} { + {mindspace}{5} + {short}{buu} + {type}{Defensive} + {gp}{25} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Biddy Amble's Bee Buzzer} { + {mindspace}{60} + {short}{babb} + {type}{Miscellaneous} + {gp}{200} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Delusions of Grandeur} { + {mindspace}{50} + {short}{dog} + {type}{Miscellaneous} + {gp}{100} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Gammer Shorga's Clever Creeper} { + {mindspace}{20} + {short}{gscc} + {type}{Miscellaneous} + {gp}{70} + {bookid}{0} + {hasbook}{true} + {components}{piece of clay (consumed), smelly seed (consumed)} + } + {Gammer Shorga's Helpful Undergrowth} { + {mindspace}{25} + {short}{gshu} + {type}{Offensive} + {gp}{70} + {bookid}{0} + {hasbook}{true} + {components}{smelly seed (consumed)} + } + {Gammer Tumult's Amalgamator} { + {mindspace}{20} + {short}{gta} + {type}{Miscellaneous} + {gp}{45} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Goodie Whemper's Apple Divination} { + {mindspace}{20} + {short}{gwad} + {type}{Miscellaneous} + {gp}{40} + {bookid}{0} + {hasbook}{true} + {components}{knife, apple (unpeeled, peel consumed)} + } + {Grammer Scorbic's Household Guard} { + {mindspace}{20} + {short}{gshg} + {type}{Defensive} + {gp}{50} + {bookid}{0} + {hasbook}{true} + {components}{utensil (to float)} + } + {Granny Beedle's Cooperative Credits} { + {mindspace}{40} + {short}{gbcc} + {type}{Miscellaneous} + {gp}{60} + {bookid}{0} + {hasbook}{true} + {components}{quill, paper (both optional)} + } + {Granny Benedict's Bond of Loyalty} { + {mindspace}{50} + {short}{gbbl} + {type}{Miscellaneous} + {gp}{80} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Granny Lipintense's Layer of Lard} { + {mindspace}{15} + {short}{glll} + {type}{Miscellaneous} + {gp}{40} + {bookid}{0} + {hasbook}{true} + {components}{eel skin, duck feather (consumed)} + } + {Hag's Blessing} { + {mindspace}{10} + {short}{hb} + {type}{Miscellaneous} + {gp}{25} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Mama Blackwing's Potent Preserver} { + {mindspace}{20} + {short}{mbpp} + {type}{Miscellaneous} + {gp}{40} + {bookid}{0} + {hasbook}{true} + {components}{frog, candle} + } + {Mama Kolydina's Instant Infestation} { + {mindspace}{45} + {short}{mkii} + {type}{Defensive} + {gp}{70} + {bookid}{0} + {hasbook}{true} + {components}{sugar cube/sugar lump (consumed)} + } + {Mother Brynda's Call of Gravity} { + {mindspace}{20} + {short}{mbcg} + {type}{Offensive} + {gp}{50} + {bookid}{0} + {hasbook}{true} + {components}{willow withe} + } + {Mother Feelbright's Busy Bees} { + {mindspace}{60} + {short}{mfbb} + {type}{Offensive} + {gp}{60} + {bookid}{0} + {hasbook}{true} + {components}{honey (consumed)} + } + {Mother Harblist's Fruity Flyer} { + {mindspace}{60} + {short}{mhff} + {type}{Miscellaneous} + {gp}{200} + {bookid}{0} + {hasbook}{true} + {components}{apple (consumed)} + } + {Mother Twinter's Yarrow Enchantment} { + {mindspace}{10} + {short}{mtye} + {type}{Miscellaneous} + {gp}{50} + {bookid}{0} + {hasbook}{true} + {components}{yarrow (consumed)} + } + {Nanny Revere's Traitorous Talisman} { + {mindspace}{50} + {short}{nrtt} + {type}{Miscellaneous} + {gp}{80} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Wee Flaudia's Fluffy Ear Muffs} { + {mindspace}{10} + {short}{wffem} + {type}{Defensive} + {gp}{30} + {bookid}{0} + {hasbook}{true} + {components}{none} + } + {Feyfirkin's Errant Trainee Collection Herbage} { + {mindspace}{25} + {short}{fetch} + {type}{Miscellaneous} + {gp}{200} + {bookid}{23} + {hasbook}{false} + {components}{wooden staff (not consumed)} + } + {Luquayle's Longevity-Enhancing Ballast} { + {mindspace}{35} + {short}{lleb} + {type}{Miscellaneous} + {gp}{150} + {bookid}{23} + {hasbook}{false} + {components}{sliver of octiron (consumed), ironwood riving (consumed)} + } + {Ridcully's Travelling Furniture Manufactory} { + {mindspace}{10} + {short}{rtfm} + {type}{Miscellaneous} + {gp}{50} + {bookid}{23} + {hasbook}{false} + {components}{none} + } + {Ralstorphine's Refreshing Draught} { + {mindspace}{5} + {short}{rrd} + {type}{Miscellaneous} + {gp}{25} + {bookid}{23} + {hasbook}{false} + {components}{none} + } +}; + +#nop --- methods_data: spell -> method -> level (1-10) -> threshold. --- ; +#var {methods_data} { + {A Cup of Tea and Sake}{{channeling}{{1}{31}{2}{41}{3}{52}{4}{58}{5}{61}{6}{74}{7}{80}{8}{97}{9}{120}{10}{156}}{divining}{{1}{94}{2}{101}{3}{110}{4}{117}{5}{123}{6}{137}{7}{142}{8}{152}{9}{184}{10}{206}}{scrying}{{1}{140}{2}{146}{3}{156}{4}{162}{5}{169}{6}{175}{7}{182}{8}{190}{9}{220}{10}{250}}} + {Al'Hrahaz's Scintillating Blorpler}{{evoking}{{1}{189}{2}{197}{3}{203}{4}{211}{5}{218}{6}{228}{7}{236}{8}{250}{9}{269}{10}{310}}{channeling}{{1}{225}{2}{232}{3}{243}{4}{249}{5}{255}{6}{265}{7}{272}{8}{285}{9}{310}{10}{340}}{evoking}{{1}{185}{2}{194}{3}{198}{4}{207}{5}{212}{6}{220}{7}{228}{8}{243}{9}{260}{10}{301}}{chanting}{{1}{203}{2}{212}{3}{221}{4}{228}{5}{234}{6}{246}{7}{252}{8}{270}{9}{280}{10}{315}}{chanting}{{1}{230}{2}{237}{3}{246}{4}{255}{5}{259}{6}{270}{7}{277}{8}{294}{9}{310}{10}{345}}} + {Amazing Silicate Blorpler}{{evoking}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{95}{9}{105}{10}{115}}{channeling}{{1}{40}{2}{45}{3}{50}{4}{55}{5}{60}{6}{65}{7}{70}{8}{80}{9}{90}{10}{100}}{evoking}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{95}{9}{105}{10}{115}}{chanting}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{85}{9}{95}{10}{105}}{chanting}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{85}{9}{90}{10}{105}}} + {Atmospheric Inscription Wonder}{{animating}{{1}{92}{2}{101}{3}{109}{4}{115}{5}{121}{6}{134}{7}{140}{8}{155}{9}{178}{10}{205}}{fire}{{1}{76}{2}{84}{3}{94}{4}{101}{5}{107}{6}{117}{7}{123}{8}{142}{9}{160}{10}{190}}{binding}{{1}{86}{2}{94}{3}{103}{4}{111}{5}{117}{6}{128}{7}{134}{8}{152}{9}{170}{10}{200}}{channeling}{{1}{84}{2}{92}{3}{101}{4}{109}{5}{115}{6}{126}{7}{132}{8}{149}{9}{165}{10}{195}}} + {Banishing of Loquacious Spirits}{{scrying}{{1}{52}{2}{55}{3}{61}{4}{68}{5}{72}{6}{79}{7}{86}{8}{101}{9}{125}{10}{154}}{abjuring}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{banishing}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}} + {Banishing of Prying Eyes}{{air}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{banishing}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{abjuring}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}} + {Banishing of Unnatural Urges}{{conjuring}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{fire}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{banishing}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}} + {Biddy Amble's Bee Buzzer}{{charming}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{dancing}{{1}{182}{2}{185}{3}{191}{4}{198}{5}{202}{6}{209}{7}{216}{8}{231}{9}{255}{10}{284}}{chanting}{{1}{182}{2}{185}{3}{191}{4}{198}{5}{202}{6}{209}{7}{216}{8}{231}{9}{255}{10}{284}}{herbal}{{1}{62}{2}{65}{3}{71}{4}{78}{5}{82}{6}{89}{7}{96}{8}{111}{9}{135}{10}{164}}} + {Bifram's Amazing Fireworks}{{animating}{{1}{90}{2}{98}{3}{108}{4}{115}{5}{121}{6}{132}{7}{139}{8}{157}{9}{179}{10}{200}}{fire}{{1}{79}{2}{87}{3}{96}{4}{104}{5}{110}{6}{121}{7}{126}{8}{146}{9}{168}{10}{190}}{binding}{{1}{106}{2}{115}{3}{124}{4}{130}{5}{137}{6}{148}{7}{153}{8}{170}{9}{190}{10}{210}}{channeling}{{1}{104}{2}{113}{3}{121}{4}{128}{5}{133}{6}{143}{7}{150}{8}{168}{9}{187}{10}{205}}} + {Booch's Extremal Polymorphism}{{channeling}{{1}{164}{2}{172}{3}{180}{4}{187}{5}{193}{6}{205}{7}{211}{8}{226}{9}{250}{10}{280}}{convoking}{{1}{164}{2}{172}{3}{180}{4}{187}{5}{193}{6}{205}{7}{211}{8}{226}{9}{250}{10}{280}}{evoking}{{1}{172}{2}{178}{3}{185}{4}{193}{5}{199}{6}{211}{7}{217}{8}{234}{9}{255}{10}{290}}{binding}{{1}{166}{2}{174}{3}{182}{4}{190}{5}{195}{6}{210}{7}{215}{8}{232}{9}{252}{10}{282}}{animating}{{1}{168}{2}{176}{3}{184}{4}{192}{5}{197}{6}{212}{7}{217}{8}{234}{9}{254}{10}{285}}} + {Boolywog's Forbidden Pleasures}{{chanting}{{1}{40}{2}{45}{3}{50}{4}{55}{5}{60}{6}{65}{7}{70}{8}{80}{9}{90}{10}{100}}{charming}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{90}{9}{100}{10}{110}}{summoning}{{1}{80}{2}{90}{3}{100}{4}{105}{5}{110}{6}{115}{7}{120}{8}{130}{9}{140}{10}{150}}} + {Brassica Oleracea Ambulata}{{evoking}{{1}{57}{2}{65}{3}{76}{4}{82}{5}{88}{6}{99}{7}{105}{8}{123}{9}{144}{10}{185}}{channeling}{{1}{61}{2}{70}{3}{78}{4}{86}{5}{91}{6}{103}{7}{109}{8}{125}{9}{148}{10}{188}}{animating}{{1}{74}{2}{82}{3}{90}{4}{99}{5}{104}{6}{115}{7}{121}{8}{138}{9}{161}{10}{198}}} + {Brother Happalon's Elementary Enchanting}{{evoking}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{90}{8}{105}{9}{120}{10}{135}}{enchanting}{{1}{70}{2}{75}{3}{80}{4}{85}{5}{90}{6}{95}{7}{105}{8}{120}{9}{135}{10}{155}}{chanting}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{95}{8}{110}{9}{130}{10}{145}}{enchanting}{{1}{75}{2}{80}{3}{90}{4}{100}{5}{105}{6}{110}{7}{120}{8}{135}{9}{150}{10}{160}}} + {Calm Embrace of Illusionary Beauty}{{conjuring}{{1}{150}{2}{155}{3}{160}{4}{170}{5}{175}{6}{180}{7}{185}{8}{190}{9}{205}{10}{220}}{charming}{{1}{160}{2}{170}{3}{180}{4}{190}{5}{195}{6}{200}{7}{205}{8}{215}{9}{225}{10}{245}}{staff}{{1}{160}{2}{170}{3}{180}{4}{190}{5}{195}{6}{200}{7}{205}{8}{215}{9}{225}{10}{245}}{air}{{1}{150}{2}{155}{3}{160}{4}{170}{5}{175}{6}{180}{7}{185}{8}{190}{9}{205}{10}{225}}} + {Cherry Blossoms in Bloom}{{air}{{1}{115}{2}{120}{3}{130}{4}{135}{5}{140}{6}{145}{7}{150}{8}{160}{9}{170}{10}{185}}{channeling}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{165}{7}{170}{8}{180}{9}{190}{10}{200}}{convoking}{{1}{115}{2}{120}{3}{130}{4}{135}{5}{140}{6}{145}{7}{150}{8}{160}{9}{170}{10}{185}}} + {Chrenedict's Corporeal Covering}{{binding}{{1}{125}{2}{130}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{165}{9}{175}{10}{180}}{earth}{{1}{130}{2}{135}{3}{145}{4}{150}{5}{155}{6}{160}{7}{165}{8}{175}{9}{180}{10}{190}}{binding}{{1}{190}{2}{200}{3}{210}{4}{215}{5}{220}{6}{225}{7}{230}{8}{240}{9}{250}{10}{270}}} + {Collatrap's Instant Pickling Stick}{{conjuring}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{110}{7}{115}{8}{125}{9}{130}{10}{140}}{evoking}{{1}{90}{2}{100}{3}{110}{4}{115}{5}{120}{6}{130}{7}{140}{8}{150}{9}{160}{10}{170}}{wand}{{1}{115}{2}{125}{3}{135}{4}{145}{5}{150}{6}{155}{7}{165}{8}{174}{9}{183}{10}{192}}{dancing}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{110}{7}{115}{8}{125}{9}{130}{10}{140}}} + {Crondor's Fabulous Detection}{{staff}{{1}{89}{2}{98}{3}{108}{4}{114}{5}{120}{6}{132}{7}{138}{8}{156}{9}{175}{10}{210}}{gold}{{1}{94}{2}{102}{3}{110}{4}{118}{5}{124}{6}{134}{7}{140}{8}{158}{9}{178}{10}{214}}{turning}{{1}{81}{2}{89}{3}{100}{4}{106}{5}{112}{6}{124}{7}{130}{8}{148}{9}{170}{10}{204}}} + {Crondor's Marvellous Sequestration}{{staff}{{1}{178}{2}{184}{3}{189}{4}{194}{5}{199}{6}{204}{7}{209}{8}{222}{9}{235}{10}{263}}{ring}{{1}{276}{2}{281}{3}{286}{4}{291}{5}{297}{6}{302}{7}{310}{8}{316}{9}{328}{10}{360}}{convoking}{{1}{202}{2}{208}{3}{214}{4}{220}{5}{226}{6}{232}{7}{240}{8}{249}{9}{268}{10}{286}}{enchanting}{{1}{275}{2}{280}{3}{285}{4}{290}{5}{295}{6}{300}{7}{305}{8}{316}{9}{330}{10}{368}}{binding}{{1}{275}{2}{281}{3}{286}{4}{291}{5}{296}{6}{303}{7}{309}{8}{320}{9}{333}{10}{364}}} + {Crondor's Mysterious Sparkling}{{staff}{{1}{100}{2}{105}{3}{110}{4}{115}{5}{125}{6}{130}{7}{140}{8}{146}{9}{160}{10}{180}}{whittling}{{1}{75}{2}{85}{3}{90}{4}{100}{5}{105}{6}{113}{7}{120}{8}{128}{9}{141}{10}{160}}{binding}{{1}{90}{2}{95}{3}{100}{4}{105}{5}{115}{6}{120}{7}{130}{8}{136}{9}{150}{10}{190}}{enchanting}{{1}{105}{2}{110}{3}{115}{4}{120}{5}{128}{6}{132}{7}{142}{8}{155}{9}{185}{10}{210}}} + {Delusions of Grandeur}{{divining}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{convoking}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{enchanting}{{1}{252}{2}{255}{3}{261}{4}{268}{5}{272}{6}{279}{7}{286}{8}{301}{9}{325}{10}{354}}} + {Dismal Digit of Doom}{{enchanting}{{1}{30}{2}{35}{3}{40}{4}{45}{5}{50}{6}{55}{7}{65}{8}{75}{9}{85}{10}{100}}{healing}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{100}{9}{115}{10}{130}}{binding}{{1}{70}{2}{75}{3}{80}{4}{85}{5}{90}{6}{100}{7}{110}{8}{120}{9}{130}{10}{140}}{animating}{{1}{105}{2}{110}{3}{115}{4}{120}{5}{130}{6}{140}{7}{150}{8}{160}{9}{170}{10}{180}}} + {Doctor Kelleflump's Deadly Demon}{{dancing}{{1}{291}{2}{300}{3}{306}{4}{315}{5}{322}{6}{330}{7}{337}{8}{355}{9}{377}{10}{414}}{cursing}{{1}{284}{2}{293}{3}{302}{4}{311}{5}{316}{6}{326}{7}{333}{8}{350}{9}{369}{10}{410}}{summoning}{{1}{293}{2}{302}{3}{313}{4}{319}{5}{324}{6}{337}{7}{344}{8}{358}{9}{381}{10}{421}}{abjuring}{{1}{295}{2}{304}{3}{315}{4}{321}{5}{326}{6}{337}{7}{344}{8}{358}{9}{381}{10}{421}}{banishing}{{1}{293}{2}{300}{3}{311}{4}{317}{5}{322}{6}{333}{7}{339}{8}{353}{9}{379}{10}{419}}} + {Doctor Worblehat's Flaming Primate Premonition}{{turning}{{1}{172}{2}{185}{3}{190}{4}{195}{5}{200}{6}{205}{7}{214}{8}{221}{9}{236}{10}{260}}{gold}{{1}{204}{2}{209}{3}{214}{4}{219}{5}{224}{6}{231}{7}{236}{8}{250}{9}{280}{10}{286}}{enchanting}{{1}{230}{2}{236}{3}{241}{4}{246}{5}{251}{6}{256}{7}{261}{8}{281}{9}{288}{10}{310}}{binding}{{1}{254}{2}{260}{3}{265}{4}{270}{5}{276}{6}{281}{7}{286}{8}{296}{9}{313}{10}{333}}} + {Duander's Thaumic Luminosity Disperser}{{chanting}{{1}{17}{2}{25}{3}{31}{4}{40}{5}{46}{6}{58}{7}{64}{8}{82}{9}{103}{10}{145}}{fire}{{1}{49}{2}{58}{3}{67}{4}{76}{5}{82}{6}{91}{7}{97}{8}{115}{9}{136}{10}{175}}{channeling}{{1}{52}{2}{61}{3}{70}{4}{79}{5}{85}{6}{94}{7}{100}{8}{118}{9}{139}{10}{178}}{banishing}{{1}{91}{2}{100}{3}{112}{4}{117}{5}{123}{6}{133}{7}{139}{8}{160}{9}{175}{10}{210}}} + {Effermhor's Hypersonic Assault}{{evoking}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{225}{10}{240}}{channeling}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{220}{10}{240}}{binding}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{220}{10}{240}}{dancing}{{1}{180}{2}{185}{3}{190}{4}{195}{5}{200}{6}{210}{7}{220}{8}{230}{9}{240}{10}{260}}{air}{{1}{180}{2}{185}{3}{190}{4}{195}{5}{200}{6}{205}{7}{210}{8}{220}{9}{240}{10}{260}}} + {Ellamandyr's Hyaline Amulet}{{wand}{{1}{127}{2}{132}{3}{138}{4}{144}{5}{150}{6}{158}{7}{164}{8}{183}{9}{210}{10}{243}}{water}{{1}{100}{2}{106}{3}{111}{4}{116}{5}{121}{6}{129}{7}{135}{8}{154}{9}{181}{10}{215}}{binding}{{1}{176}{2}{182}{3}{190}{4}{195}{5}{200}{6}{208}{7}{214}{8}{233}{9}{260}{10}{291}}{silver}{{1}{96}{2}{101}{3}{106}{4}{111}{5}{116}{6}{124}{7}{130}{8}{149}{9}{176}{10}{210}}} + {Endorphin's Floating Friend}{{evoking}{{1}{80}{2}{90}{3}{100}{4}{105}{5}{110}{6}{120}{7}{130}{8}{140}{9}{150}{10}{160}}{animating}{{1}{110}{2}{115}{3}{125}{4}{135}{5}{140}{6}{150}{7}{160}{8}{170}{9}{180}{10}{185}}{binding}{{1}{120}{2}{130}{3}{140}{4}{150}{5}{160}{6}{170}{7}{180}{8}{190}{9}{200}{10}{210}}} + {Eringyas' Surprising Bouquet}{{channeling}{{1}{55}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{95}{9}{100}{10}{110}}{evoking}{{1}{40}{2}{45}{3}{50}{4}{55}{5}{60}{6}{65}{7}{70}{8}{75}{9}{80}{10}{90}}{charming}{{1}{30}{2}{35}{3}{40}{4}{45}{5}{50}{6}{55}{7}{60}{8}{65}{9}{70}{10}{80}}} + {Fabrication Classification Identification}{{divining}{{1}{57}{2}{65}{3}{74}{4}{83}{5}{88}{6}{98}{7}{105}{8}{122}{9}{147}{10}{184}}{evoking}{{1}{76}{2}{83}{3}{94}{4}{99}{5}{105}{6}{116}{7}{122}{8}{140}{9}{162}{10}{202}}{convoking}{{1}{72}{2}{81}{3}{87}{4}{94}{5}{101}{6}{112}{7}{118}{8}{138}{9}{158}{10}{197}}} + {Feyfirkin's Errant Trainee Collection Herbage}{{divining}{{1}{285}{2}{289}{3}{293}{4}{298}{5}{302}{6}{310}{7}{315}{8}{325}{9}{340}{10}{375}}{staff}{{1}{230}{2}{234}{3}{239}{4}{245}{5}{247}{6}{255}{7}{260}{8}{270}{9}{288}{10}{309}}{convoking}{{1}{274}{2}{279}{3}{284}{4}{289}{5}{294}{6}{300}{7}{306}{8}{315}{9}{333}{10}{353}}{channeling}{{1}{219}{2}{223}{3}{228}{4}{233}{5}{238}{6}{243}{7}{249}{8}{260}{9}{280}{10}{300}}{evoking}{{1}{238}{2}{243}{3}{248}{4}{254}{5}{258}{6}{264}{7}{269}{8}{279}{9}{294}{10}{318}}} + {Fiddelmaker's Auriferous Embrace}{{wand}{{1}{200}{2}{205}{3}{215}{4}{220}{5}{230}{6}{240}{7}{250}{8}{260}{9}{270}{10}{290}}{gold}{{1}{210}{2}{220}{3}{230}{4}{235}{5}{240}{6}{250}{7}{260}{8}{270}{9}{280}{10}{300}}{animating}{{1}{200}{2}{205}{3}{215}{4}{220}{5}{230}{6}{240}{7}{250}{8}{260}{9}{270}{10}{290}}} + {Finneblaugh's Thaumic Float}{{air}{{1}{175}{2}{180}{3}{190}{4}{200}{5}{205}{6}{210}{7}{220}{8}{230}{9}{240}{10}{260}}{evoking}{{1}{190}{2}{200}{3}{210}{4}{215}{5}{225}{6}{240}{7}{255}{8}{265}{9}{275}{10}{285}}{summoning}{{1}{200}{2}{210}{3}{220}{4}{225}{5}{235}{6}{250}{7}{265}{8}{270}{9}{280}{10}{290}}{air}{{1}{215}{2}{225}{3}{235}{4}{245}{5}{255}{6}{260}{7}{270}{8}{280}{9}{290}{10}{300}}} + {Floron's Fabulous Mirror}{{channeling}{{1}{115}{2}{124}{3}{130}{4}{139}{5}{144}{6}{154}{7}{160}{8}{178}{9}{196}{10}{240}}{divining}{{1}{124}{2}{129}{3}{139}{4}{145}{5}{154}{6}{160}{7}{169}{8}{187}{9}{208}{10}{245}}{scrying}{{1}{199}{2}{205}{3}{214}{4}{223}{5}{229}{6}{238}{7}{244}{8}{262}{9}{286}{10}{315}}} + {Friddlefrod's Hydratic Extrusion}{{water}{{1}{45}{2}{50}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{90}{9}{100}{10}{110}}{evoking}{{1}{60}{2}{70}{3}{80}{4}{85}{5}{90}{6}{95}{7}{100}{8}{110}{9}{120}{10}{130}}{water}{{1}{80}{2}{90}{3}{100}{4}{105}{5}{110}{6}{115}{7}{120}{8}{125}{9}{130}{10}{140}}} + {Frygellhan's Fiendish Orbit Disruptor}{{evoking}{{1}{75}{2}{80}{3}{90}{4}{95}{5}{100}{6}{105}{7}{115}{8}{120}{9}{130}{10}{140}}{channeling}{{1}{120}{2}{125}{3}{135}{4}{140}{5}{150}{6}{155}{7}{160}{8}{170}{9}{175}{10}{185}}{binding}{{1}{130}{2}{140}{3}{150}{4}{160}{5}{170}{6}{180}{7}{190}{8}{200}{9}{210}{10}{225}}} + {Fyodor's Nimbus of Porterage}{{water}{{1}{105}{2}{110}{3}{120}{4}{125}{5}{130}{6}{135}{7}{140}{8}{150}{9}{160}{10}{170}}{fire}{{1}{90}{2}{95}{3}{100}{4}{110}{5}{115}{6}{120}{7}{130}{8}{140}{9}{150}{10}{160}}{air}{{1}{110}{2}{120}{3}{125}{4}{130}{5}{135}{6}{140}{7}{150}{8}{164}{9}{183}{10}{200}}{summoning}{{1}{125}{2}{130}{3}{140}{4}{145}{5}{150}{6}{160}{7}{175}{8}{184}{9}{203}{10}{215}}{abjuring}{{1}{90}{2}{95}{3}{100}{4}{110}{5}{115}{6}{120}{7}{130}{8}{140}{9}{150}{10}{160}}{channeling}{{1}{150}{2}{160}{3}{165}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{215}{10}{230}}} + {G'flott's Olfactory Nightmare}{{evoking}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{195}{8}{205}{9}{220}{10}{240}}{air}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{195}{8}{205}{9}{220}{10}{240}}{dancing}{{1}{170}{2}{175}{3}{180}{4}{185}{5}{190}{6}{195}{7}{205}{8}{215}{9}{230}{10}{250}}{fire}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{195}{8}{205}{9}{220}{10}{240}}{air}{{1}{180}{2}{185}{3}{190}{4}{195}{5}{200}{6}{205}{7}{210}{8}{220}{9}{240}{10}{260}}} + {Gammer Shorga's Clever Creeper}{{charming}{{1}{277}{2}{280}{3}{286}{4}{293}{5}{297}{6}{304}{7}{311}{8}{326}{9}{350}{10}{379}}{shaping}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{ring}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{animating}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}} + {Gammer Shorga's Helpful Undergrowth}{{animating}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{cursing}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}} + {Gammer Tumult's Amalgamator}{{evoking}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{weaving}{{1}{52}{2}{55}{3}{61}{4}{68}{5}{72}{6}{79}{7}{86}{8}{101}{9}{125}{10}{154}}{binding}{{1}{192}{2}{195}{3}{201}{4}{208}{5}{212}{6}{219}{7}{226}{8}{241}{9}{265}{10}{294}}} + {Gillimer's Ring of Temperate Weather}{{gold}{{1}{191}{2}{200}{3}{211}{4}{218}{5}{223}{6}{232}{7}{240}{8}{260}{9}{282}{10}{315}}{binding}{{1}{207}{2}{216}{3}{224}{4}{231}{5}{238}{6}{249}{7}{254}{8}{273}{9}{295}{10}{330}}{enchanting}{{1}{194}{2}{200}{3}{211}{4}{218}{5}{223}{6}{236}{7}{242}{8}{260}{9}{282}{10}{318}}{ring}{{1}{194}{2}{200}{3}{211}{4}{218}{5}{223}{6}{236}{7}{242}{8}{260}{9}{282}{10}{318}}} + {Goodie Whemper's Apple Divination}{{channeling}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{divining}{{1}{77}{2}{80}{3}{86}{4}{93}{5}{97}{6}{104}{7}{111}{8}{126}{9}{150}{10}{179}}} + {Grammer Scorbic's Household Guard}{{evoking}{{1}{62}{2}{65}{3}{71}{4}{78}{5}{82}{6}{89}{7}{96}{8}{111}{9}{135}{10}{164}}{channeling}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{binding}{{1}{177}{2}{180}{3}{186}{4}{193}{5}{197}{6}{204}{7}{211}{8}{226}{9}{250}{10}{279}}} + {Granny Beedle's Cooperative Credits}{{talisman}{{1}{82}{2}{85}{3}{91}{4}{98}{5}{102}{6}{109}{7}{116}{8}{131}{9}{155}{10}{184}}{chanting}{{1}{112}{2}{115}{3}{121}{4}{128}{5}{132}{6}{139}{7}{146}{8}{161}{9}{185}{10}{214}}{scrying}{{1}{172}{2}{175}{3}{181}{4}{188}{5}{192}{6}{199}{7}{206}{8}{221}{9}{245}{10}{274}}{divining}{{1}{162}{2}{165}{3}{171}{4}{178}{5}{182}{6}{189}{7}{196}{8}{211}{9}{235}{10}{264}}} + {Granny Benedict's Bond of Loyalty}{{talisman}{{1}{62}{2}{65}{3}{71}{4}{78}{5}{82}{6}{89}{7}{96}{8}{111}{9}{135}{10}{164}}{summoning}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{charming}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{binding}{{1}{127}{2}{130}{3}{136}{4}{143}{5}{147}{6}{154}{7}{161}{8}{176}{9}{200}{10}{229}}} + {Granny Lipintense's Layer of Lard}{{dancing}{{1}{117}{2}{120}{3}{126}{4}{133}{5}{137}{6}{144}{7}{151}{8}{166}{9}{190}{10}{219}}{charming}{{1}{112}{2}{115}{3}{121}{4}{128}{5}{132}{6}{139}{7}{146}{8}{161}{9}{185}{10}{214}}{channeling}{{1}{147}{2}{150}{3}{156}{4}{163}{5}{167}{6}{174}{7}{181}{8}{196}{9}{220}{10}{249}}} + {Grisald's Chilly Touch}{{summoning}{{1}{60}{2}{70}{3}{80}{4}{85}{5}{90}{6}{95}{7}{100}{8}{110}{9}{120}{10}{130}}{healing}{{1}{80}{2}{90}{3}{100}{4}{105}{5}{110}{6}{115}{7}{120}{8}{130}{9}{140}{10}{150}}} + {Grisald's Reanimated Guardian}{{summoning}{{1}{170}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{210}{9}{230}{10}{255}}{binding}{{1}{170}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{210}{9}{230}{10}{255}}{animating}{{1}{155}{2}{160}{3}{165}{4}{170}{5}{175}{6}{180}{7}{185}{8}{190}{9}{205}{10}{215}}{healing}{{1}{260}{2}{270}{3}{275}{4}{280}{5}{285}{6}{295}{7}{305}{8}{315}{9}{325}{10}{335}}} + {Gryntard's Feathery Reliever}{{summoning}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{165}{7}{170}{8}{180}{9}{190}{10}{210}}{air}{{1}{140}{2}{150}{3}{160}{4}{170}{5}{180}{6}{185}{7}{195}{8}{205}{9}{220}{10}{240}}{binding}{{1}{125}{2}{135}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{170}{9}{180}{10}{200}}{channeling}{{1}{155}{2}{160}{3}{170}{4}{175}{5}{180}{6}{185}{7}{195}{8}{205}{9}{220}{10}{240}}} + {Hag's Blessing}{{divining}{{1}{77}{2}{80}{3}{86}{4}{93}{5}{97}{6}{104}{7}{111}{8}{126}{9}{150}{10}{179}}{charming}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{summoning}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}} + {Heezlewurst's Elemental Buffer}{{channeling}{{1}{150}{2}{155}{3}{165}{4}{170}{5}{175}{6}{185}{7}{195}{8}{205}{9}{215}{10}{230}}{evoking}{{1}{170}{2}{180}{3}{185}{4}{190}{5}{200}{6}{205}{7}{215}{8}{225}{9}{235}{10}{250}}{binding}{{1}{155}{2}{165}{3}{170}{4}{175}{5}{180}{6}{190}{7}{200}{8}{210}{9}{220}{10}{240}}{enchanting}{{1}{155}{2}{165}{3}{170}{4}{175}{5}{180}{6}{190}{7}{200}{8}{210}{9}{220}{10}{240}}{convoking}{{1}{185}{2}{190}{3}{200}{4}{210}{5}{220}{6}{230}{7}{240}{8}{250}{9}{260}{10}{280}}} + {Independent Recurring Vocaliser}{{channeling}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{85}{8}{95}{9}{105}{10}{120}}{binding}{{1}{90}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{130}{8}{140}{9}{150}{10}{160}}{evoking}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{95}{8}{105}{9}{115}{10}{130}}} + {Jogloran's Portal of Cheaper Travel}{{enchanting}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{170}{7}{180}{8}{192}{9}{205}{10}{220}}{evoking}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{170}{7}{180}{8}{192}{9}{205}{10}{220}}{binding}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{170}{7}{180}{8}{192}{9}{210}{10}{225}}{dancing}{{1}{130}{2}{140}{3}{150}{4}{155}{5}{160}{6}{170}{7}{180}{8}{192}{9}{210}{10}{225}}{banishing}{{1}{160}{2}{165}{3}{170}{4}{180}{5}{190}{6}{200}{7}{210}{8}{222}{9}{235}{10}{250}}} + {Jorodin's Magnificent Communicator}{{channeling}{{1}{159}{2}{168}{3}{178}{4}{183}{5}{188}{6}{200}{7}{206}{8}{224}{9}{248}{10}{284}}{binding}{{1}{156}{2}{166}{3}{176}{4}{182}{5}{188}{6}{200}{7}{205}{8}{222}{9}{241}{10}{281}}{evoking}{{1}{123}{2}{130}{3}{140}{4}{146}{5}{154}{6}{166}{7}{171}{8}{188}{9}{212}{10}{248}}} + {Journey of the Heavenly Storm Dragon}{{water}{{1}{320}{2}{334}{3}{344}{4}{349}{5}{354}{6}{365}{7}{372}{8}{389}{9}{410}{10}{449}}{air}{{1}{320}{2}{334}{3}{344}{4}{349}{5}{354}{6}{365}{7}{372}{8}{389}{9}{410}{10}{449}}{earth}{{1}{320}{2}{334}{3}{344}{4}{349}{5}{354}{6}{365}{7}{372}{8}{389}{9}{410}{10}{449}}{fire}{{1}{320}{2}{334}{3}{344}{4}{349}{5}{354}{6}{365}{7}{372}{8}{389}{9}{410}{10}{449}}{channeling}{{1}{355}{2}{365}{3}{370}{4}{380}{5}{385}{6}{395}{7}{401}{8}{420}{9}{440}{10}{480}}} + {Kamikaze Oryctolagus Flammula}{{convoking}{{1}{90}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{130}{8}{140}{9}{150}{10}{160}}{fire}{{1}{90}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{130}{8}{140}{9}{150}{10}{160}}{convoking}{{1}{110}{2}{120}{3}{130}{4}{135}{5}{140}{6}{145}{7}{150}{8}{160}{9}{170}{10}{185}}{fire}{{1}{90}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{130}{8}{140}{9}{150}{10}{160}}} + {Kelleflump's Irritating Demon}{{summoning}{{1}{120}{2}{125}{3}{130}{4}{135}{5}{140}{6}{145}{7}{150}{8}{160}{9}{165}{10}{175}}{cursing}{{1}{130}{2}{135}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{165}{9}{180}{10}{205}}{conjuring}{{1}{135}{2}{140}{3}{145}{4}{150}{5}{155}{6}{160}{7}{165}{8}{175}{9}{190}{10}{215}}{divining}{{1}{120}{2}{125}{3}{130}{4}{135}{5}{140}{6}{145}{7}{150}{8}{160}{9}{170}{10}{180}}} + {Kipperwald's Perlustration Prevention}{{convoking}{{1}{190}{2}{200}{3}{210}{4}{215}{5}{220}{6}{225}{7}{230}{8}{240}{9}{250}{10}{270}}{scrying}{{1}{210}{2}{220}{3}{230}{4}{240}{5}{250}{6}{255}{7}{260}{8}{270}{9}{280}{10}{290}}{divining}{{1}{200}{2}{205}{3}{210}{4}{220}{5}{230}{6}{235}{7}{245}{8}{255}{9}{265}{10}{280}}} + {Luquayle's Longevity-Enhancing Ballast}{{gold}{{1}{250}{2}{254}{3}{258}{4}{264}{5}{269}{6}{275}{7}{281}{8}{291}{9}{306}{10}{329}}{turning}{{1}{214}{2}{218}{3}{223}{4}{228}{5}{233}{6}{239}{7}{245}{8}{255}{9}{270}{10}{293}}{enchanting}{{1}{195}{2}{200}{3}{204}{4}{208}{5}{213}{6}{218}{7}{225}{8}{235}{9}{250}{10}{275}}{water}{{1}{178}{2}{182}{3}{187}{4}{193}{5}{200}{6}{204}{7}{209}{8}{219}{9}{235}{10}{261}}{binding}{{1}{214}{2}{218}{3}{223}{4}{228}{5}{233}{6}{239}{7}{245}{8}{255}{9}{270}{10}{292}}} + {Malich's AshkEnte Circle}{{channeling}{{1}{181}{2}{186}{3}{192}{4}{198}{5}{204}{6}{211}{7}{219}{8}{228}{9}{243}{10}{270}}{chanting}{{1}{175}{2}{182}{3}{190}{4}{195}{5}{200}{6}{205}{7}{215}{8}{225}{9}{240}{10}{270}}{talisman}{{1}{100}{2}{106}{3}{114}{4}{130}{5}{140}{6}{152}{7}{160}{8}{170}{9}{180}{10}{190}}{staff}{{1}{175}{2}{183}{3}{191}{4}{196}{5}{202}{6}{211}{7}{220}{8}{232}{9}{253}{10}{280}}} + {Malich's AshkEnte Summoning Incantation}{{channeling}{{1}{290}{2}{304}{3}{314}{4}{324}{5}{334}{6}{344}{7}{354}{8}{364}{9}{380}{10}{400}}{abjuring}{{1}{300}{2}{310}{3}{320}{4}{330}{5}{340}{6}{350}{7}{360}{8}{370}{9}{390}{10}{410}}{summoning}{{1}{320}{2}{330}{3}{340}{4}{350}{5}{360}{6}{370}{7}{380}{8}{395}{9}{405}{10}{435}}} + {Malich's Penetrating Ocular Lance}{{channeling}{{1}{30}{2}{35}{3}{40}{4}{45}{5}{50}{6}{55}{7}{60}{8}{65}{9}{70}{10}{80}}} + {Mama Blackwing's Potent Preserver}{{fire}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{chanting}{{1}{127}{2}{130}{3}{136}{4}{143}{5}{147}{6}{154}{7}{161}{8}{176}{9}{200}{10}{229}}{channeling}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{water}{{1}{147}{2}{150}{3}{156}{4}{163}{5}{167}{6}{174}{7}{181}{8}{196}{9}{220}{10}{249}}} + {Mama Kolydina's Instant Infestation}{{summoning}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{charming}{{1}{92}{2}{95}{3}{101}{4}{108}{5}{112}{6}{119}{7}{126}{8}{141}{9}{165}{10}{194}}{chanting}{{1}{112}{2}{115}{3}{121}{4}{128}{5}{132}{6}{139}{7}{146}{8}{161}{9}{185}{10}{214}}{binding}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{cursing}{{1}{87}{2}{90}{3}{96}{4}{103}{5}{107}{6}{114}{7}{121}{8}{136}{9}{160}{10}{189}}} + {Master Glimer's Amazing Glowing Thing}{{evoking}{{1}{81}{2}{93}{3}{99}{4}{109}{5}{114}{6}{126}{7}{132}{8}{147}{9}{171}{10}{206}}{earth}{{1}{126}{2}{135}{3}{140}{4}{145}{5}{153}{6}{165}{7}{171}{8}{189}{9}{216}{10}{252}}{chanting}{{1}{99}{2}{108}{3}{117}{4}{123}{5}{132}{6}{141}{7}{147}{8}{160}{9}{174}{10}{210}}{binding}{{1}{147}{2}{153}{3}{162}{4}{168}{5}{177}{6}{183}{7}{193}{8}{205}{9}{230}{10}{270}}{enchanting}{{1}{203}{2}{208}{3}{219}{4}{225}{5}{234}{6}{243}{7}{248}{8}{270}{9}{291}{10}{325}}} + {Master Woddeley's Luminescent Companion}{{evoking}{{1}{53}{2}{63}{3}{74}{4}{79}{5}{84}{6}{97}{7}{102}{8}{124}{9}{144}{10}{182}}{channeling}{{1}{82}{2}{92}{3}{100}{4}{106}{5}{113}{6}{123}{7}{131}{8}{149}{9}{170}{10}{202}}{animating}{{1}{100}{2}{106}{3}{116}{4}{123}{5}{129}{6}{142}{7}{148}{8}{162}{9}{186}{10}{216}}} + {Memories of a Vicious Chicken}{{healing}{{1}{55}{2}{60}{3}{65}{4}{70}{5}{75}{6}{80}{7}{85}{8}{90}{9}{100}{10}{110}}{summoning}{{1}{55}{2}{60}{3}{65}{4}{70}{5}{75}{6}{80}{7}{85}{8}{90}{9}{100}{10}{110}}{animating}{{1}{70}{2}{75}{3}{80}{4}{85}{5}{90}{6}{95}{7}{100}{8}{110}{9}{120}{10}{130}}} + {Mother Brynda's Call of Gravity}{{evoking}{{1}{62}{2}{65}{3}{71}{4}{78}{5}{82}{6}{89}{7}{96}{8}{111}{9}{135}{10}{164}}{channeling}{{1}{122}{2}{125}{3}{131}{4}{138}{5}{142}{6}{149}{7}{156}{8}{171}{9}{195}{10}{224}}{binding}{{1}{177}{2}{180}{3}{186}{4}{193}{5}{197}{6}{204}{7}{211}{8}{226}{9}{250}{10}{279}}} + {Mother Feelbright's Busy Bees}{{channeling}{{1}{142}{2}{145}{3}{151}{4}{158}{5}{162}{6}{169}{7}{176}{8}{191}{9}{215}{10}{244}}{summoning}{{1}{177}{2}{180}{3}{186}{4}{193}{5}{197}{6}{204}{7}{211}{8}{226}{9}{250}{10}{279}}{cursing}{{1}{107}{2}{110}{3}{116}{4}{123}{5}{127}{6}{134}{7}{141}{8}{156}{9}{180}{10}{209}}{charming}{{1}{197}{2}{200}{3}{206}{4}{213}{5}{217}{6}{224}{7}{231}{8}{246}{9}{270}{10}{299}}} + {Mother Harblist's Fruity Flyer}{{summoning}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{chanting}{{1}{252}{2}{255}{3}{261}{4}{268}{5}{272}{6}{279}{7}{286}{8}{301}{9}{325}{10}{354}}{charming}{{1}{202}{2}{205}{3}{211}{4}{218}{5}{222}{6}{229}{7}{236}{8}{251}{9}{275}{10}{304}}{dancing}{{1}{272}{2}{275}{3}{281}{4}{288}{5}{292}{6}{299}{7}{306}{8}{321}{9}{345}{10}{374}}} + {Mother Twinter's Yarrow Enchantment}{{evoking}{{1}{27}{2}{30}{3}{36}{4}{43}{5}{47}{6}{54}{7}{61}{8}{76}{9}{100}{10}{129}}{enchanting}{{1}{52}{2}{55}{3}{61}{4}{68}{5}{72}{6}{79}{7}{86}{8}{101}{9}{125}{10}{154}}{chanting}{{1}{52}{2}{55}{3}{61}{4}{68}{5}{72}{6}{79}{7}{86}{8}{101}{9}{125}{10}{154}}{enchanting}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}} + {Mugwuddle's Muddling Mirage}{{air}{{1}{75}{2}{85}{3}{90}{4}{100}{5}{105}{6}{115}{7}{125}{8}{140}{9}{160}{10}{205}}{animating}{{1}{95}{2}{105}{3}{110}{4}{120}{5}{130}{6}{140}{7}{145}{8}{160}{9}{185}{10}{220}}{summoning}{{1}{165}{2}{175}{3}{180}{4}{190}{5}{195}{6}{205}{7}{210}{8}{229}{9}{251}{10}{290}}{channeling}{{1}{135}{2}{144}{3}{153}{4}{160}{5}{165}{6}{176}{7}{181}{8}{200}{9}{220}{10}{260}}{conjuring}{{1}{178}{2}{185}{3}{194}{4}{202}{5}{210}{6}{219}{7}{225}{8}{240}{9}{265}{10}{300}}} + {Myrandil's Mask of Death}{{enchanting}{{1}{81}{2}{88}{3}{98}{4}{105}{5}{112}{6}{121}{7}{128}{8}{147}{9}{169}{10}{200}}{healing}{{1}{84}{2}{93}{3}{103}{4}{108}{5}{115}{6}{126}{7}{131}{8}{150}{9}{171}{10}{203}}{summoning}{{1}{107}{2}{115}{3}{123}{4}{130}{5}{136}{6}{147}{7}{152}{8}{171}{9}{192}{10}{220}}{binding}{{1}{115}{2}{123}{3}{131}{4}{138}{5}{144}{6}{155}{7}{160}{8}{179}{9}{200}{10}{228}}{abjuring}{{1}{123}{2}{131}{3}{139}{4}{146}{5}{152}{6}{163}{7}{168}{8}{187}{9}{208}{10}{236}}} + {Myrandil's Vicious Seizure}{{summoning}{{1}{140}{2}{150}{3}{155}{4}{165}{5}{170}{6}{175}{7}{180}{8}{185}{9}{195}{10}{210}}{earth}{{1}{140}{2}{150}{3}{155}{4}{165}{5}{170}{6}{175}{7}{180}{8}{185}{9}{195}{10}{210}}{healing}{{1}{180}{2}{190}{3}{195}{4}{200}{5}{210}{6}{220}{7}{230}{8}{240}{9}{250}{10}{265}}{animating}{{1}{145}{2}{150}{3}{155}{4}{160}{5}{170}{6}{175}{7}{180}{8}{185}{9}{200}{10}{220}}} + {Nanny Revere's Traitorous Talisman}{{talisman}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{divining}{{1}{152}{2}{155}{3}{161}{4}{168}{5}{172}{6}{179}{7}{186}{8}{201}{9}{225}{10}{254}}{chanting}{{1}{252}{2}{255}{3}{261}{4}{268}{5}{272}{6}{279}{7}{286}{8}{301}{9}{325}{10}{354}}{charming}{{1}{302}{2}{305}{3}{311}{4}{318}{5}{322}{6}{329}{7}{336}{8}{351}{9}{375}{10}{404}}} + {Nargl'frob's Empyrean Spear}{{water}{{1}{170}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{224}{9}{247}{10}{286}}{air}{{1}{165}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{221}{9}{243}{10}{282}}{fire}{{1}{165}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{219}{9}{240}{10}{278}}{earth}{{1}{170}{2}{180}{3}{185}{4}{190}{5}{195}{6}{200}{7}{205}{8}{226}{9}{249}{10}{288}}{evoking}{{1}{170}{2}{175}{3}{180}{4}{185}{5}{190}{6}{195}{7}{200}{8}{227}{9}{250}{10}{288}}} + {Narquin's Hand of Acquisition}{{convoking}{{1}{112}{2}{120}{3}{128}{4}{135}{5}{143}{6}{151}{7}{159}{8}{176}{9}{197}{10}{230}}{healing}{{1}{89}{2}{99}{3}{107}{4}{114}{5}{120}{6}{130}{7}{138}{8}{154}{9}{174}{10}{209}}{animating}{{1}{122}{2}{130}{3}{140}{4}{147}{5}{152}{6}{163}{7}{170}{8}{188}{9}{209}{10}{243}}} + {Narquin's Mist of Doom}{{summoning}{{1}{95}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{135}{8}{145}{9}{155}{10}{165}}{healing}{{1}{105}{2}{110}{3}{120}{4}{125}{5}{130}{6}{135}{7}{140}{8}{150}{9}{155}{10}{165}}{abjuring}{{1}{95}{2}{100}{3}{110}{4}{115}{5}{120}{6}{125}{7}{135}{8}{145}{9}{155}{10}{165}}{animating}{{1}{120}{2}{130}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{170}{9}{175}{10}{180}}} + {Objandeller's Thaumic Funnel}{{water}{{1}{77}{2}{85}{3}{95}{4}{102}{5}{108}{6}{120}{7}{125}{8}{142}{9}{165}{10}{195}}{channeling}{{1}{91}{2}{100}{3}{108}{4}{116}{5}{122}{6}{132}{7}{138}{8}{157}{9}{178}{10}{202}}{dancing}{{1}{79}{2}{87}{3}{95}{4}{104}{5}{110}{6}{120}{7}{126}{8}{144}{9}{164}{10}{190}}{binding}{{1}{81}{2}{89}{3}{98}{4}{106}{5}{112}{6}{122}{7}{128}{8}{146}{9}{165}{10}{192}}} + {Old Bellicus' Brazen Knuckles}{{evoking}{{1}{92}{2}{99}{3}{108}{4}{115}{5}{120}{6}{129}{7}{139}{8}{152}{9}{170}{10}{200}}{binding}{{1}{80}{2}{86}{3}{97}{4}{104}{5}{109}{6}{118}{7}{127}{8}{142}{9}{160}{10}{190}}{earth}{{1}{95}{2}{105}{3}{115}{4}{121}{5}{127}{6}{139}{7}{144}{8}{162}{9}{180}{10}{210}}{fire}{{1}{59}{2}{68}{3}{77}{4}{83}{5}{92}{6}{104}{7}{109}{8}{127}{9}{145}{10}{180}}{binding}{{1}{83}{2}{92}{3}{101}{4}{106}{5}{112}{6}{124}{7}{130}{8}{147}{9}{165}{10}{195}}} + {Patient Taming of the Quantum Weather Butterfly}{{water}{{1}{154}{2}{161}{3}{173}{4}{178}{5}{183}{6}{194}{7}{202}{8}{217}{9}{240}{10}{278}}{earth}{{1}{179}{2}{187}{3}{194}{4}{202}{5}{210}{6}{221}{7}{227}{8}{244}{9}{266}{10}{300}}{air}{{1}{248}{2}{255}{3}{267}{4}{274}{5}{279}{6}{289}{7}{297}{8}{316}{9}{335}{10}{370}}{fire}{{1}{250}{2}{257}{3}{269}{4}{276}{5}{281}{6}{291}{7}{299}{8}{318}{9}{337}{10}{372}}{binding}{{1}{297}{2}{305}{3}{316}{4}{324}{5}{329}{6}{339}{7}{346}{8}{362}{9}{380}{10}{410}}} + {Polliwiggle's Puissancy Probe}{{divining}{{1}{32}{2}{40}{3}{50}{4}{57}{5}{63}{6}{73}{7}{79}{8}{98}{9}{119}{10}{160}}{divining}{{1}{48}{2}{57}{3}{65}{4}{73}{5}{78}{6}{90}{7}{96}{8}{112}{9}{135}{10}{171}}} + {Pragi's Fiery Gaze}{{evoking}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{215}{10}{230}}{channeling}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{180}{6}{185}{7}{190}{8}{200}{9}{220}{10}{235}}{enchanting}{{1}{170}{2}{175}{3}{180}{4}{185}{5}{190}{6}{195}{7}{205}{8}{215}{9}{230}{10}{240}}{animating}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{185}{6}{195}{7}{200}{8}{205}{9}{220}{10}{235}}{fire}{{1}{185}{2}{190}{3}{195}{4}{200}{5}{205}{6}{210}{7}{215}{8}{225}{9}{240}{10}{255}}} + {Pragi's Lost Gaze}{{evoking}{{1}{130}{2}{135}{3}{145}{4}{155}{5}{160}{6}{170}{7}{180}{8}{190}{9}{200}{10}{210}}{evoking}{{1}{130}{2}{135}{3}{145}{4}{155}{5}{160}{6}{170}{7}{180}{8}{190}{9}{200}{10}{210}}{enchanting}{{1}{130}{2}{135}{3}{145}{4}{155}{5}{160}{6}{170}{7}{180}{8}{190}{9}{200}{10}{210}}{animating}{{1}{140}{2}{145}{3}{155}{4}{165}{5}{170}{6}{180}{7}{190}{8}{200}{9}{210}{10}{220}}{animating}{{1}{140}{2}{145}{3}{155}{4}{165}{5}{170}{6}{180}{7}{190}{8}{200}{9}{210}{10}{220}}{fire}{{1}{130}{2}{135}{3}{145}{4}{155}{5}{160}{6}{170}{7}{180}{8}{190}{9}{200}{10}{210}}} + {Pragi's Molten Gaze}{{animating}{{1}{100}{2}{105}{3}{110}{4}{115}{5}{120}{6}{125}{7}{130}{8}{135}{9}{145}{10}{165}}{fire}{{1}{115}{2}{120}{3}{125}{4}{135}{5}{140}{6}{150}{7}{155}{8}{165}{9}{178}{10}{199}}{evoking}{{1}{90}{2}{95}{3}{100}{4}{105}{5}{110}{6}{115}{7}{125}{8}{135}{9}{145}{10}{165}}{channeling}{{1}{110}{2}{115}{3}{120}{4}{130}{5}{135}{6}{145}{7}{150}{8}{160}{9}{175}{10}{195}}} + {Professor Flambardie's Grim Amulet}{{amulet}{{1}{130}{2}{135}{3}{140}{4}{145}{5}{150}{6}{155}{7}{165}{8}{180}{9}{200}{10}{240}}{water}{{1}{200}{2}{210}{3}{220}{4}{225}{5}{230}{6}{240}{7}{250}{8}{265}{9}{280}{10}{305}}{wand}{{1}{195}{2}{205}{3}{215}{4}{220}{5}{225}{6}{235}{7}{245}{8}{260}{9}{275}{10}{300}}{summoning}{{1}{225}{2}{230}{3}{240}{4}{245}{5}{250}{6}{260}{7}{270}{8}{285}{9}{300}{10}{350}}{binding}{{1}{220}{2}{230}{3}{240}{4}{250}{5}{260}{6}{270}{7}{280}{8}{290}{9}{305}{10}{355}}} + {Ralstorphine's Refreshing Draught}{{water}{{1}{54}{2}{58}{3}{63}{4}{68}{5}{72}{6}{78}{7}{83}{8}{94}{9}{109}{10}{135}}{evoking}{{1}{80}{2}{84}{3}{89}{4}{93}{5}{97}{6}{104}{7}{109}{8}{121}{9}{134}{10}{155}}{summoning}{{1}{54}{2}{58}{3}{63}{4}{68}{5}{72}{6}{78}{7}{83}{8}{94}{9}{109}{10}{135}}} + {Reckless Encouragement of Arcane Peacock}{{air}{{1}{200}{2}{206}{3}{216}{4}{225}{5}{230}{6}{241}{7}{246}{8}{264}{9}{287}{10}{320}}{channeling}{{1}{225}{2}{235}{3}{243}{4}{250}{5}{257}{6}{267}{7}{273}{8}{290}{9}{310}{10}{340}}{divining}{{1}{275}{2}{284}{3}{293}{4}{302}{5}{307}{6}{316}{7}{325}{8}{340}{9}{360}{10}{390}}{binding}{{1}{250}{2}{260}{3}{268}{4}{273}{5}{282}{6}{293}{7}{299}{8}{316}{9}{339}{10}{360}}{enchanting}{{1}{270}{2}{281}{3}{290}{4}{300}{5}{305}{6}{313}{7}{320}{8}{336}{9}{350}{10}{380}}} + {Ridcully's Travelling Furniture Manufactory}{{chanting}{{1}{104}{2}{109}{3}{114}{4}{119}{5}{122}{6}{129}{7}{136}{8}{145}{9}{159}{10}{180}}{dancing}{{1}{104}{2}{109}{3}{114}{4}{119}{5}{122}{6}{129}{7}{136}{8}{145}{9}{159}{10}{180}}{conjuring}{{1}{104}{2}{109}{3}{114}{4}{119}{5}{122}{6}{129}{7}{136}{8}{145}{9}{159}{10}{180}}{banishing}{{1}{104}{2}{109}{3}{114}{4}{119}{5}{122}{6}{129}{7}{136}{8}{145}{9}{159}{10}{180}}{charming}{{1}{104}{2}{109}{3}{114}{4}{119}{5}{122}{6}{129}{7}{136}{8}{145}{9}{159}{10}{180}}} + {Rubayak's Power Dispenser}{{enchanting}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{105}{7}{115}{8}{125}{9}{140}{10}{170}}{channeling}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{105}{7}{115}{8}{125}{9}{140}{10}{170}}{binding}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{105}{7}{115}{8}{125}{9}{140}{10}{170}}} + {Rubayak's Power Storage}{{binding}{{1}{64}{2}{74}{3}{83}{4}{89}{5}{95}{6}{108}{7}{115}{8}{130}{9}{152}{10}{189}}{channeling}{{1}{161}{2}{167}{3}{177}{4}{183}{5}{189}{6}{202}{7}{208}{8}{224}{9}{240}{10}{273}}{enchanting}{{1}{92}{2}{102}{3}{111}{4}{118}{5}{124}{6}{136}{7}{142}{8}{158}{9}{180}{10}{200}}{enchanting}{{1}{152}{2}{161}{3}{167}{4}{177}{5}{183}{6}{193}{7}{199}{8}{216}{9}{239}{10}{270}}} + {Rugged Victor's Rodentia Vivisection}{{charming}{{1}{59}{2}{64}{3}{71}{4}{77}{5}{83}{6}{89}{7}{95}{8}{100}{9}{112}{10}{125}}{dancing}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{85}{9}{90}{10}{100}}} + {Sageroff's Sentry Summoning}{{fire}{{1}{111}{2}{122}{3}{131}{4}{136}{5}{142}{6}{153}{7}{159}{8}{175}{9}{201}{10}{237}}{charming}{{1}{114}{2}{125}{3}{134}{4}{139}{5}{142}{6}{156}{7}{162}{8}{178}{9}{204}{10}{240}}{summoning}{{1}{131}{2}{139}{3}{147}{4}{156}{5}{161}{6}{170}{7}{178}{8}{197}{9}{217}{10}{252}}{abjuring}{{1}{91}{2}{97}{3}{105}{4}{114}{5}{120}{6}{130}{7}{170}{8}{180}{9}{190}{10}{212}}{banishing}{{1}{144}{2}{153}{3}{164}{4}{170}{5}{175}{6}{187}{7}{192}{8}{209}{9}{234}{10}{268}}} + {Scolorid's Scintillating Scribbling}{{evoking}{{1}{80}{2}{90}{3}{100}{4}{110}{5}{120}{6}{130}{7}{140}{8}{150}{9}{160}{10}{170}}{binding}{{1}{85}{2}{95}{3}{105}{4}{115}{5}{125}{6}{135}{7}{145}{8}{155}{9}{165}{10}{175}}{convoking}{{1}{90}{2}{100}{3}{110}{4}{120}{5}{130}{6}{140}{7}{150}{8}{160}{9}{170}{10}{180}}{enchanting}{{1}{100}{2}{110}{3}{120}{4}{130}{5}{140}{6}{150}{7}{160}{8}{170}{9}{180}{10}{190}}} + {Skeetbraskin's Fuliginous Perdition}{{summoning}{{1}{119}{2}{125}{3}{134}{4}{139}{5}{146}{6}{155}{7}{164}{8}{182}{9}{203}{10}{236}}{abjuring}{{1}{203}{2}{208}{3}{218}{4}{224}{5}{233}{6}{242}{7}{248}{8}{266}{9}{287}{10}{320}}{banishing}{{1}{182}{2}{197}{3}{203}{4}{209}{5}{215}{6}{224}{7}{233}{8}{251}{9}{272}{10}{308}}{conjuring}{{1}{175}{2}{180}{3}{185}{4}{191}{5}{196}{6}{209}{7}{214}{8}{230}{9}{250}{10}{293}}} + {Sorklin's Field of Protection}{{evoking}{{1}{60}{2}{65}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{95}{9}{100}{10}{110}}{air}{{1}{50}{2}{55}{3}{60}{4}{65}{5}{70}{6}{75}{7}{80}{8}{85}{9}{90}{10}{100}}{channeling}{{1}{70}{2}{75}{3}{80}{4}{85}{5}{90}{6}{95}{7}{100}{8}{105}{9}{110}{10}{120}}} + {Sorsalsean's Seismic Eruption}{{earth}{{1}{176}{2}{183}{3}{192}{4}{198}{5}{204}{6}{215}{7}{221}{8}{233}{9}{256}{10}{285}}{evoking}{{1}{170}{2}{180}{3}{189}{4}{195}{5}{202}{6}{211}{7}{219}{8}{230}{9}{250}{10}{280}}{chanting}{{1}{176}{2}{183}{3}{192}{4}{198}{5}{204}{6}{215}{7}{221}{8}{233}{9}{256}{10}{285}}{channeling}{{1}{170}{2}{180}{3}{189}{4}{195}{5}{202}{6}{211}{7}{219}{8}{230}{9}{250}{10}{280}}{dancing}{{1}{150}{2}{159}{3}{160}{4}{176}{5}{181}{6}{192}{7}{198}{8}{215}{9}{235}{10}{250}}} + {Stacklady's Morphic Resonator}{{channeling}{{1}{280}{2}{290}{3}{303}{4}{308}{5}{316}{6}{326}{7}{331}{8}{345}{9}{365}{10}{390}}{evoking}{{1}{224}{2}{233}{3}{241}{4}{250}{5}{255}{6}{264}{7}{270}{8}{290}{9}{300}{10}{340}}{enchanting}{{1}{240}{2}{250}{3}{260}{4}{265}{5}{270}{6}{284}{7}{290}{8}{300}{9}{326}{10}{364}}} + {Thousand Dancing Celestial Fates}{{dancing}{{1}{120}{2}{130}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{165}{9}{170}{10}{180}}{turning}{{1}{120}{2}{130}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{165}{9}{170}{10}{180}}{whittling}{{1}{120}{2}{130}{3}{140}{4}{145}{5}{150}{6}{155}{7}{160}{8}{165}{9}{170}{10}{180}}{enchanting}{{1}{140}{2}{150}{3}{160}{4}{165}{5}{170}{6}{175}{7}{180}{8}{185}{9}{190}{10}{200}}{binding}{{1}{140}{2}{150}{3}{160}{4}{165}{5}{170}{6}{175}{7}{180}{8}{185}{9}{190}{10}{200}}} + {Torqvald's Illusion Generatrix}{{air}{{1}{188}{2}{196}{3}{206}{4}{212}{5}{217}{6}{227}{7}{236}{8}{252}{9}{271}{10}{300}}{binding}{{1}{196}{2}{206}{3}{212}{4}{220}{5}{225}{6}{236}{7}{241}{8}{260}{9}{280}{10}{312}}{conjuring}{{1}{190}{2}{197}{3}{206}{4}{215}{5}{220}{6}{230}{7}{236}{8}{254}{9}{274}{10}{307}}{charming}{{1}{170}{2}{179}{3}{187}{4}{196}{5}{201}{6}{211}{7}{217}{8}{236}{9}{257}{10}{290}}{channeling}{{1}{203}{2}{211}{3}{223}{4}{227}{5}{233}{6}{244}{7}{252}{8}{268}{9}{288}{10}{318}}{conjuring}{{1}{185}{2}{193}{3}{203}{4}{209}{5}{217}{6}{227}{7}{233}{8}{252}{9}{270}{10}{298}}{charming}{{1}{233}{2}{241}{3}{250}{4}{257}{5}{263}{6}{274}{7}{279}{8}{298}{9}{317}{10}{346}}} + {Torqvald's Many Colours}{{air}{{1}{50}{2}{60}{3}{70}{4}{75}{5}{80}{6}{85}{7}{90}{8}{100}{9}{110}{10}{120}}{channeling}{{1}{60}{2}{70}{3}{80}{4}{90}{5}{100}{6}{105}{7}{110}{8}{120}{9}{130}{10}{140}}{charming}{{1}{70}{2}{80}{3}{90}{4}{100}{5}{105}{6}{110}{7}{115}{8}{125}{9}{135}{10}{145}}} + {Transcendent Pneumatic Alleviator}{{evoking}{{1}{90}{2}{95}{3}{100}{4}{105}{5}{110}{6}{120}{7}{130}{8}{140}{9}{150}{10}{165}}{air}{{1}{105}{2}{110}{3}{120}{4}{125}{5}{130}{6}{140}{7}{145}{8}{150}{9}{160}{10}{175}}{chanting}{{1}{105}{2}{110}{3}{120}{4}{125}{5}{130}{6}{140}{7}{150}{8}{160}{9}{170}{10}{185}}{enchanting}{{1}{105}{2}{110}{3}{120}{4}{125}{5}{130}{6}{140}{7}{150}{8}{160}{9}{170}{10}{180}}{channeling}{{1}{140}{2}{145}{3}{150}{4}{155}{5}{160}{6}{170}{7}{180}{8}{185}{9}{200}{10}{210}}} + {Turnwhistle's Effulgent Autiridescence}{{divining}{{1}{138}{2}{143}{3}{150}{4}{155}{5}{160}{6}{165}{7}{175}{8}{185}{9}{195}{10}{210}}{convoking}{{1}{176}{2}{194}{3}{203}{4}{214}{5}{220}{6}{225}{7}{230}{8}{240}{9}{250}{10}{270}}{evoking}{{1}{200}{2}{210}{3}{215}{4}{220}{5}{225}{6}{235}{7}{245}{8}{255}{9}{265}{10}{280}}{binding}{{1}{200}{2}{210}{3}{215}{4}{220}{5}{225}{6}{235}{7}{245}{8}{255}{9}{265}{10}{280}}} + {Union of the Phoenix and Divine Dragon}{{channeling}{{1}{145}{2}{150}{3}{160}{4}{165}{5}{170}{6}{180}{7}{185}{8}{195}{9}{205}{10}{215}}{summoning}{{1}{150}{2}{160}{3}{170}{4}{175}{5}{180}{6}{185}{7}{195}{8}{205}{9}{215}{10}{235}}{charming}{{1}{180}{2}{185}{3}{195}{4}{200}{5}{205}{6}{215}{7}{225}{8}{235}{9}{245}{10}{255}}} + {Von Hasselhoff's Skin Condition}{{convoking}{{1}{160}{2}{166}{3}{175}{4}{183}{5}{190}{6}{199}{7}{205}{8}{225}{9}{240}{10}{270}}{healing}{{1}{144}{2}{150}{3}{159}{4}{166}{5}{172}{6}{185}{7}{190}{8}{208}{9}{230}{10}{260}}{charming}{{1}{125}{2}{134}{3}{141}{4}{148}{5}{155}{6}{167}{7}{172}{8}{187}{9}{200}{10}{245}}{binding}{{1}{160}{2}{166}{3}{175}{4}{183}{5}{190}{6}{199}{7}{205}{8}{225}{9}{240}{10}{270}}} + {Wee Flaudia's Fluffy Ear Muffs}{{chanting}{{1}{102}{2}{105}{3}{111}{4}{118}{5}{122}{6}{129}{7}{136}{8}{151}{9}{175}{10}{204}}{channeling}{{1}{112}{2}{115}{3}{121}{4}{128}{5}{132}{6}{139}{7}{146}{8}{161}{9}{185}{10}{214}}{air}{{1}{132}{2}{135}{3}{141}{4}{148}{5}{152}{6}{159}{7}{166}{8}{181}{9}{205}{10}{234}}} + {Wonker's Wicked Wobble}{{gold}{{1}{260}{2}{270}{3}{280}{4}{290}{5}{300}{6}{310}{7}{320}{8}{330}{9}{340}{10}{350}}{binding}{{1}{160}{2}{165}{3}{170}{4}{175}{5}{185}{6}{200}{7}{215}{8}{230}{9}{245}{10}{260}}{rod}{{1}{170}{2}{175}{3}{185}{4}{190}{5}{200}{6}{205}{7}{215}{8}{225}{9}{235}{10}{250}}{enchanting}{{1}{170}{2}{175}{3}{185}{4}{190}{5}{200}{6}{205}{7}{215}{8}{225}{9}{235}{10}{250}}} + {Worstler's Advanced Metallurgical Glance}{{animating}{{1}{9}{2}{17}{3}{24}{4}{33}{5}{38}{6}{50}{7}{56}{8}{74}{9}{95}{10}{133}}{divining}{{1}{50}{2}{58}{3}{66}{4}{73}{5}{79}{6}{90}{7}{98}{8}{115}{9}{135}{10}{169}}{scrying}{{1}{57}{2}{65}{3}{73}{4}{80}{5}{86}{6}{97}{7}{105}{8}{122}{9}{142}{10}{176}}} + {Worstler's Elementary Mineralogical Glance}{{earth}{{1}{30}{2}{35}{3}{40}{4}{45}{5}{50}{6}{55}{7}{60}{8}{70}{9}{80}{10}{90}}{evoking}{{1}{40}{2}{45}{3}{50}{4}{55}{5}{60}{6}{65}{7}{70}{8}{80}{9}{90}{10}{100}}{scrying}{{1}{70}{2}{80}{3}{90}{4}{95}{5}{100}{6}{105}{7}{110}{8}{115}{9}{125}{10}{135}}} + {Wungle's Body Part Suggestion}{{summoning}{{1}{40}{2}{49}{3}{52}{4}{65}{5}{71}{6}{83}{7}{89}{8}{106}{9}{130}{10}{170}}{healing}{{1}{32}{2}{40}{3}{50}{4}{55}{5}{61}{6}{74}{7}{80}{8}{98}{9}{119}{10}{158}}{animating}{{1}{122}{2}{129}{3}{136}{4}{144}{5}{150}{6}{162}{7}{169}{8}{186}{9}{200}{10}{230}}{binding}{{1}{71}{2}{80}{3}{89}{4}{96}{5}{101}{6}{113}{7}{119}{8}{136}{9}{158}{10}{190}}} + {Wungle's Great Sucking}{{summoning}{{1}{170}{2}{175}{3}{180}{4}{190}{5}{200}{6}{205}{7}{210}{8}{220}{9}{230}{10}{250}}{chanting}{{1}{170}{2}{175}{3}{180}{4}{190}{5}{200}{6}{205}{7}{210}{8}{220}{9}{230}{10}{250}}{healing}{{1}{170}{2}{180}{3}{190}{4}{200}{5}{205}{6}{210}{7}{220}{8}{230}{9}{240}{10}{260}}{cursing}{{1}{185}{2}{195}{3}{200}{4}{210}{5}{220}{6}{225}{7}{235}{8}{244}{9}{255}{10}{270}}} + {Wurphle's Midnight Snack}{{channeling}{{1}{102}{2}{110}{3}{121}{4}{127}{5}{133}{6}{144}{7}{150}{8}{169}{9}{189}{10}{221}}{evoking}{{1}{119}{2}{125}{3}{135}{4}{141}{5}{148}{6}{158}{7}{165}{8}{183}{9}{202}{10}{238}}{convoking}{{1}{119}{2}{127}{3}{135}{4}{144}{5}{150}{6}{160}{7}{167}{8}{183}{9}{204}{10}{238}}} + {Wurphle's Packed Lunch}{{animating}{{1}{256}{2}{259}{3}{263}{4}{268}{5}{274}{6}{280}{7}{286}{8}{303}{9}{315}{10}{355}}{enchanting}{{1}{304}{2}{310}{3}{313}{4}{319}{5}{324}{6}{331}{7}{334}{8}{347}{9}{359}{10}{388}}{whittling}{{1}{355}{2}{360}{3}{363}{4}{370}{5}{373}{6}{378}{7}{385}{8}{395}{9}{411}{10}{434}}{binding}{{1}{304}{2}{310}{3}{313}{4}{319}{5}{324}{6}{331}{7}{336}{8}{347}{9}{359}{10}{388}}} + {Yordon's Extremal Extension}{{channeling}{{1}{71}{2}{78}{3}{88}{4}{95}{5}{103}{6}{111}{7}{118}{8}{135}{9}{157}{10}{195}}{convoking}{{1}{76}{2}{85}{3}{93}{4}{100}{5}{106}{6}{116}{7}{123}{8}{140}{9}{162}{10}{199}}{evoking}{{1}{79}{2}{88}{3}{95}{4}{102}{5}{109}{6}{118}{7}{125}{8}{142}{9}{165}{10}{202}}{binding}{{1}{95}{2}{102}{3}{111}{4}{118}{5}{123}{6}{135}{7}{140}{8}{159}{9}{181}{10}{215}}{animating}{{1}{111}{2}{118}{3}{128}{4}{135}{5}{140}{6}{152}{7}{157}{8}{175}{9}{197}{10}{230}}} +}; + +#nop --- sAcronymToSpell: acronym -> full spell name. Built on load from spell_data[][short] + secondaries (excl. rtfm1, quow, fetch1). --- ; +#var {acronym_to_spell} {}; +#foreach {*spell_data[%*]} {_sp} { + #var {_sh} {$spell_data[$_sp][short]}; + #var {acronym_to_spell[$_sh]} {$_sp}; +}; +#var {acronym_to_spell[boa1]} {Brassica Oleracea Ambulata}; +#var {acronym_to_spell[dog1]} {Delusions of Grandeur}; +#var {acronym_to_spell[kid1]} {Kelleflump's Irritating Demon}; +#var {acronym_to_spell[reap1]} {Reckless Encouragement of Arcane Peacock}; +#var {acronym_to_spell[tea1]} {Turnwhistle's Effulgent Autiridescence}; +#var {acronym_to_spell[fae1]} {Fiddelmaker's Auriferous Embrace}; + +#class {spelldata} {close}; diff --git a/src/missions.tin b/src/missions.tin index 8b82388..0b73a76 100644 --- a/src/missions.tin +++ b/src/missions.tin @@ -4,7 +4,33 @@ #NOP ==[ Load Individual Mission stuff ]== #READ {src/missions/sausages.tin}; #READ {src/missions/letters.tin}; +#READ {src/missions/sailing.tin}; +#READ {src/missions/jobmarket.tin}; +#VARIABLE {missiontimers_file} {logs/features/missiontimers_sync.tin}; + + +#ALIAS {^missions help$} { + #showme {[missions] Mission timers help:<099>}; + #showme {Mission timers track the cooldown period for various missions across multiple characters.}; + #showme {}; + #showme {Supported mission cooldowns:<099>}; + #showme { *<099> AM Job Market (Mr Werks)<099>}; + #showme { *<099> Dead Letter Office (Frank)<099>}; + #showme { *<099> Sam Slager Sausage Jobs<099>}; + #showme { *<099> Sailing Cooldown (Chidder)<099>}; + #showme {}; + #showme {Available commands:<099>}; + #showme { *<099> "missions<099>" - show active cooldowns (all characters)<099>}; + #showme { *<099> "missions help<099>" - show this helpfile<099>}; + #showme { *<099> "mtsave<099>" - save active cooldowns to file<099>}; + #showme { *<099> "mtload<099>" - load active cooldowns from file<099>}; + #showme {}; + #showme {When a timer runs out, a message will print to inform which character and mission are ready to do again.}; + #showme {}; +}; + +#NOP ==[ Missions command reports on each modules active/retry lists ]==; #ALIAS {missions} { #if {&missions == 0} { #return; @@ -13,125 +39,113 @@ #format {current_time} {%T}; #var {mission_display_list} {}; + #nop ==============================; + #nop ===== Sailing Missions =====; + #nop ==============================; + #if {&missions[sailing]} { + #foreach {*missions[sailing][active_list][]} {player} { + #var {mins} {@ts_to_min{$current_time;$missions[sailing][active_list][$player]}}; + #math {mins} {120 - $mins}; + + #math {hours} {$mins / 60}; + #math {mins} {$mins % 60}; + + #if {$mins < 0} { + #unvar {missions[sailing][active_list][$player]}; + }; + #else { + #if {$hours <= 0} { + #format {mt_display} {\t<138>*<099> %s<099> can do another sailing mission in <139>%d<099> minute(s).<099>} {$player}{$mins}; + }; + #else { + #format {mt_display} {\t<138>*<099> %s<099> can do another sailing mission in <139>%d<099> hour and <139>%d<099> minute(s).<099>} {$player}{$hours}{$mins}; + }; + #list {mission_display_list[$player]} {add} {$mt_display}; + }; + }; + }; + + #nop ==============================; #nop ===== Sausage Missions =====; #nop ==============================; #if {&missions[sausage]} { - #foreach {$missions[sausage][active_list][]} {player} { + #foreach {*missions[sausage][active_list][]} {player} { #if {&missions[sausage][active_list][$player][easy]} { - #math {mins} {$current_time - $missions[sausage][active_list][$player][easy]}; - #math {mins} {($mins - (60 - 1)) / 60}; - + #var {mins} {@ts_to_min{$current_time;$missions[sausage][active_list][$player][easy]}}; #math {mins} {60 - $mins}; - #format {mt_display} {\t<129>%s<099> can do another unspiced sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; + #format {mt_display} {\t<138>*<099> %s<099> can do another unspiced sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; #list {mission_display_list[$player]} {add} {$mt_display}; }; #if {&missions[sausage][active_list][$player][hard]} { - #math {mins} {$current_time - $missions[sausage][active_list][$player][hard]}; - #math {mins} {($mins - (60 - 1)) / 60}; - + #var {mins} {@ts_to_min{$current_time;$missions[sausage][active_list][$player][hard]}}; #math {mins} {60 - $mins}; - #format {mt_display} {\t<129>%s<099> can do another spiced sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; + #format {mt_display} {\t<138>*<099> %s<099> can do another spiced sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; #list {mission_display_list[$player]} {add} {$mt_display}; }; }; - #foreach {$missions[sausage][retry_list][]} {player} { - #math {mins} {$current_time - $missions[sausage][retry_list][$player]}; - #math {mins} {($mins - (60 - 1)) / 60}; - + #foreach {*missions[sausage][retry_list][]} {player} { + #var {mins} {@ts_to_min{$current_time;$missions[sausage][retry_list][$player]}}; #math {mins} {10 - $mins}; - #format {mt_display} {\t<129>%s<099> can request a new sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; + #format {mt_display} {\t<138>*<099> %s<099> can request a new sausage mission in <139>%d<099> minutes.<099>} {$player}{$mins}; #list {mission_display_list[$player]} {add} {$mt_display}; }; }; + #nop =================================; + #nop ===== Job Market Missions =====; + #nop =================================; + #if {&missions[jobmarket]} { + #foreach {*missions[jobmarket][active_list][]} {player} { + #foreach {*missions[jobmarket][active_list][$player][]} {job} { + #var {mins} {@ts_to_min{$current_time;$missions[jobmarket][active_list][$player][$job]}}; + #math {mins} {60 - $mins}; + + #format {mt_display} {\t<138>*<099> %s<099> can do another Job Market "%c%s%c" mission in <139>%d<099> minutes.<099>} {$player}{Jade}{$job}{<099>}{$mins}; + #list {mission_display_list[$player]} {add} {$mt_display}; + }; + }; + + #foreach {*missions[jobmarket][retry_list][]} {player} { + #var {mins} {@ts_to_min{$current_time;$missions[jobmarket][retry_list][$player]}}; + #math {mins} {10 - $mins}; + + #format {mt_display} {\t<138>*<099> %s<099> can request a new Job Market mission in <139>%d<099> minutes.<099>} {$player}{$mins}; + #list {mission_display_list[$player]} {add} {$mt_display}; + }; + }; + + #nop ==============================; #nop ===== Dead Letters =====; #nop ==============================; #if {&missions[letter]} { - #foreach {$missions[letter][active_list][]} {player} { - #if {&missions[letter][active_list][$player][difficult]} { - #math {mins} {$current_time - $missions[letter][active_list][$player][difficult]}; - #math {mins} {($mins - (60 - 1)) / 60}; - + #foreach {*missions[letter][active_list][]} {player} { + #foreach {*missions[letter][active_list][$player][]} {difficulty} { + #var {mins} {@ts_to_min{$current_time;$missions[letter][active_list][$player][$difficulty]}}; #math {mins} {60 - $mins}; #if {$mins < 0} { - #unvar {missions[letter][active_list][$player][difficult]}; + #unvar {missions[letter][active_list][$player][$difficulty]}; } { - #format {mt_display} {\t<129>%s<099> can do another difficult customer letter in <139>%d<099> minutes.<099>} {$player}{$mins}; - #list {mission_display_list[$player]} {add} {$mt_display}; - }; - }; - #if {&missions[letter][active_list][$player][distant]} { - #math {mins} {$current_time - $missions[letter][active_list][$player][distant]}; - #math {mins} {($mins - (60 - 1)) / 60}; - - #math {mins} {60 - $mins}; - - #if {$mins < 0} { - #unvar {missions[letter][active_list][$player][distant]}; - } { - #format {mt_display} {\t<129>%s<099> can do another distant lands letter in <139>%d<099> minutes.<099>} {$player}{$mins}; - #list {mission_display_list[$player]} {add} {$mt_display}; - }; - }; - #if {&missions[letter][active_list][$player][far]} { - #math {mins} {$current_time - $missions[letter][active_list][$player][far]}; - #math {mins} {($mins - (60 - 1)) / 60}; - - #math {mins} {60 - $mins}; - - #if {$mins < 0} { - #unvar {missions[letter][active_list][$player][far]}; - } { - #format {mt_display} {\t<129>%s<099> can do another far away letter in <139>%d<099> minutes.<099>} {$player}{$mins}; - #list {mission_display_list[$player]} {add} {$mt_display}; - }; - }; - #if {&missions[letter][active_list][$player][close]} { - #math {mins} {$current_time - $missions[letter][active_list][$player][close]}; - #math {mins} {($mins - (60 - 1)) / 60}; - - #math {mins} {60 - $mins}; - - #if {$mins < 0} { - #unvar {missions[letter][active_list][$player][close]}; - } { - #format {mt_display} {\t<129>%s<099> can do another close-by letter in <139>%d<099> minutes.<099>} {$player}{$mins}; - #list {mission_display_list[$player]} {add} {$mt_display}; - }; - }; - #if {&missions[letter][active_list][$player][local]} { - #math {mins} {$current_time - $missions[letter][active_list][$player][local]}; - #math {mins} {($mins - (60 - 1)) / 60}; - - #math {mins} {60 - $mins}; - - #if {$mins < 0} { - #unvar {missions[letter][active_list][$player][local]}; - } { - #format {mt_display} {\t<129>%s<099> can do another local letter in <139>%d<099> minutes.<099>} {$player}{$mins}; + #format {mt_display} {\t<138>*<099> %s<099> can do another "<188>%s<099>" letter in <139>%d<099> minutes.<099>} {$player}{$letter_types[$difficulty]}{$mins}; #list {mission_display_list[$player]} {add} {$mt_display}; }; }; }; - - #foreach {$missions[letter][retry_list][]} {player} { - #math {mins} {$current_time - $missions[letter][retry_list][$player]}; - #math {mins} {($mins - (60 - 1)) / 60}; - + #foreach {*missions[letter][retry_list][]} {player} { + #var {mins} {@ts_to_min{$current_time;$missions[letter][retry_list][$player]}}; #math {mins} {10 - $mins}; #if {$mins < 0} { #unvar {missions[letter][retry_list][player]}; } { - #format {mt_display} {\t<129>%s<099> can request another letter in <139>%d<099> minutes.<099>} {$player}{$mins}; + #format {mt_display} {\t<138>*<099> %s<099> can request another letter in <139>%d<099> minutes.<099>} {$player}{$mins}; #list {mission_display_list[$player]} {add} {$mt_display}; }; }; @@ -144,16 +158,59 @@ #nop ==============================; #list {mission_display_list} {size} {mdl_count}; #if {$mdl_count > 0} { - #echo {<149>[MT] Active Mission Timers:<099>}; - #foreach {$mission_display_list[]} {player} { + #echo {[missions] Active Mission Timers:<099>}; + #foreach {*mission_display_list[]} {player} { #foreach {$mission_display_list[$player][%*]} {mt_display} { #showme {$mt_display}; }; }; } { - #echo {<149>[MT] No current mission timers.<099>}; + #echo {[missions] No current mission timers.<099>}; }; }; +#FUNCTION {ts_to_min} { + #nop @ts_to_min{\;}; + #math {mins} {%1 - %2}; + #math {mins} {($mins - (60 - 1)) / 60}; + + #return {$mins}; +}; + +#NOP ==[ read/write from file for persistence ]==; +#ALIAS {mtsave} { + /writemissiontimerstofile; + + #format {mtsync_message} {[missions] <138>*<900> Saved all available mission timers from %cthis session%c to the disk.<099>} {Green}{}; + #echo {$mtsync_message}; +}; + +#ALIAS {/writemissiontimerstofile} { + #class {mtsync} {clear}; + #class {mtsync} {open}; + #var {missions_sync} {$missions}; + #class {mtsync} {close}; + + #system {touch $missiontimers_file}; + #class {mtsync} {write} {$missiontimers_file}; +} + +#ALIAS {mtload} { + #class {mtsync} {clear}; + #class {mtsync} {read} {$missiontimers_file}; + + #nop == overwrite mission variable with loaded data; + #variable {missions} {$missions_sync}; + + #nop == recreate delays for all mission modules; + /sausage_recreate_delays; + /sailing_recreate_delays; + /letters_recreate_delays; + /jobmarket_recreate_delays; + + #format {mtsync_message} {[missions] <138>*<900> Synced all available mission timers from %clast session%c off the disk.<099>} {Green}{<188>}; + #echo {$mtsync_message}; +}; + #CLASS {missiontimers} {close}; diff --git a/src/missions/jobmarket.tin b/src/missions/jobmarket.tin new file mode 100644 index 0000000..3c47e33 --- /dev/null +++ b/src/missions/jobmarket.tin @@ -0,0 +1,151 @@ +#CLASS {missiontimers_jobmarket} {kill}; +#CLASS {missiontimers_jobmarket} {open}; + +#VARIABLE {missions[jobmarket]} { + {board_codes}{} + {current_code}{0} + {current_name}{0} +}; + +#NOP Trigger to capture what "code" belongs to what job when reading the board; +#ACTION {^{[A-Z]{2}}) {Thirst Quencher|Gofer|Dog Walker|Advertising Manager|Street Fixer|Sett Maker|Delivery Lackey|Delivery Runner|Full-time Street Repairer|Textiles Fixer|Expert Street Maintenance Technician|Deliverer of Rare Items|Deliverer of Foreign Goods}$} { + #VARIABLE {missions[jobmarket][board_codes][%1]} {%2}; +}; + + +#NOP Alias to capture when we apply for a job at Mr Werks; +#ALIAS {^apply for{ job|} {[A-z]{2}}$} { + #FORMAT {jobcode} {%u} {%2}; + #SEND {apply for job $jobcode}; + + #NOP Reference code in last known board_codes table to get a job name; + #VARIABLE {missions[jobmarket][current_code]} {$jobcode}; + #VARIABLE {jobname} {$missions[jobmarket][board_codes][$jobcode]}; + #VARIABLE {missions[jobmarket][current_name]} {$jobname}; +}; + +#NOP Alias to capture when we resign from a job; +#ALIAS {^resign from job} { + #SEND {resign from job}; + #LOCAL {current_name} {$missions[jobmarket][current_name]}; + #IF {"$current_name" != "0"} { + #UNVARIABLE {missions[jobmarket][active_list][$GMCP[charinfo][capname]][$current_name]}; + #FORMAT {missions[jobmarket][retry_list][$GMCP[charinfo][capname]]} {%T}; + + #line sub variable { + #delay {600} { + #format {jobmarket_timer_line} {[missions] <138>*<900> %s can now apply for another Job Market mission.<099>} {$GMCP[charinfo][capname]}; + #echo {$jobmarket_timer_line}; + #unvar {$jobmarket_timer_line}; + }; + }; + #unvar {delayname}; + }; +}; + +#NOP Trigger to capture the job being accepted so we get an accurate start-time stamp; +#ACTION {Mr Werks hands you an official employment writ.} { + #NOP Set a timestamp for when we began this job; + #VARIABLE {missions[jobmarket][current_name]} {$jobname}; + #IF {"$missions[jobmarket][current_name]" != "0"} { + #FORMAT {missions[jobmarket][active_list][$GMCP[charinfo][capname]][$missions[jobmarket][current_name]]} {%T}; + }; +}; + +#NOP Trigger to capture time on the job expiring; +#ACTION {^The time has expired on your present job. You do not need to finish the rest of your deliveries, Mr Werks will find someone else to do this.$} { + #format {current_time} {%T}; + #format {current_player} {%s} {$GMCP[charinfo][capname]}; + #format {current_job} {%s} {$missions[jobmarket][current_name]}; + #variable {missions[jobmarket][active_list][$current_player][$current_job]} {$current_time}; + + + #nop == configure a message display when the timer is up ==; + #math {mins} {$current_time - $missions[jobmarket][active_list][$current_player][$current_job]}; + #math {mins} {($mins - (60 - 1)) / 60}; + + #math {time_left} {60 - $mins}; + #line sub variable { + #delay {$time_left} { + #format {jobmarket_timer_line} {[missions] <138>*<900> %s can now do another Job Market "%c%s%c" mission.<099>} {$GMCP[charinfo][capname]} {Jade} {$missions[jobmarket][current_name]} {}; + #echo {$jobmarket_timer_line}; + #unvar {$jobmarket_timer_line}; + + #unvar {missions[jobmarket][active_list][$current_player][$current_job]}; + }; + }; + #unvar {delayname}; +}; + +#NOP Trigger to capture reward from completing a job; +#ACTION {^You have been awarded {[0-9]{1,7}} experience points {for|for partially} completing this job%3} { + #nop Mission completed, so store current mission for current player in active_list; + #format {current_time} {%T}; + #format {current_player} {%s} {$GMCP[charinfo][capname]}; + #format {current_job} {%s} {$missions[jobmarket][current_name]}; + #variable {missions[jobmarket][active_list][$current_player][$current_job]} {$current_time}; + + + #nop == configure a message display when the timer is up ==; + #line sub variable { + #format {delayname} {jobmarket_%s_%s} {$current_player} {$missions[jobmarket][current_name]}; + #delay {$delayname} { + #format {jobmarket_timer_line} {[missions] <138>*<900> %s can now do another Job Market "%c%s%c" mission.<099>} {$GMCP[charinfo][capname]} {Jade} {$missions[jobmarket][current_name]} {}; + #echo {$jobmarket_timer_line}; + #unvar {$jobmarket_timer_line}; + + #unvar {missions[jobmarket][active_list][$current_player][$current_job]}; + } {3600}; + }; + #unvar {delayname}; +}; + +#ALIAS {/jobmarket_recreate_delays} { + #format {current_time} {%T}; + + #nop == start with retry list; + #foreach {*missions[jobmarket][retry_list][]} {player} { + #local {delay_time} {$missions[jobmarket][retry_list][$player]}; + #math {delay_time} {600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[jobmarket][retry_list][$player]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {jobmarket_timer_line} {[missions] <138>*<900> %s can now apply for another Job Market mission.<099>} {$player}; + #echo {$jobmarket_timer_line}; + #unvar {jobmarket_timer_line}; + + #unvar {missions[jobmarket][retry_list][$player]}; + }; + }; + }; + #nop == do all the difficulties; + #foreach {*missions[jobmarket][active_list][]} {player} { + #foreach {*missions[jobmarket][active_list][$player][]} {$job} { + #local {delay_time} {$missions[jobmarket][active_list][$player][$job]}; + #math {delay_time} {3600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[jobmarket][active_list][$player][$difficulty]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {jobmarket_timer_line} {[missions] <138>*<900> %s can now do another Job Market "%c%s%c" mission.<099>} {$player} {Jade} {$job} {}; + #echo {$jobmarket_timer_line}; + #unvar {jobmarket_timer_line}; + + #unvar {missions[jobmarket][active_list][$player][$difficulty]}; + }; + }; + }; + }; +}; + + +#CLASS {missiontimers_jobmarket} {close}; diff --git a/src/missions/letters.tin b/src/missions/letters.tin index 198c3c5..9d52215 100644 --- a/src/missions/letters.tin +++ b/src/missions/letters.tin @@ -1,43 +1,38 @@ #CLASS {missiontimers_letters} {kill}; #CLASS {missiontimers_letters} {open}; -#NOP ======================= -#NOP ===== VARIABLES ===== -#NOP ======================= - -#NOP [1 = Local, 2 = Close-by, 3 = Far Away, 4 = Distant Lands, 5 = Difficult Customer] -#VAR {dead_letter_rewards} { - {4 Ankh-Morpork dollars}{1} - {8 Ankh-Morpork dollars}{2} - {3 Djelian talents and 5 Djelian tooni}{3} - {4 Lancre crowns and 2 Lancre shillings}{3} - {7 Ephebian decadrachmae and 2 Ephebian stater}{3} - {2 Ankh-Morpork ten-dollars}{3} - {2 Genuan forins and 5 Genuan livres}{3} - {1 Klatchian ten dinar and 2 Klatchian two dinar coins}{3} - {1 Agatean Empire ten-rhinu and 5 Agatean Empire rhinu}{3} - {7 Lancre crowns and 5 Lancre shillings}{4} - {2 Agatean Empire ten-rhinu and 5 Agatean Empire rhinu}{4} - {6 Djelian talents and 5 Djelian tooni}{4} - {4 Genuan forins and 3 Genuan livres}{4} - {3 Ankh-Morpork ten-dollars and 2 Ankh-Morpork dollars}{4} - {1 Ephebian mina and 5 Ephebian decadrachmae}{4} - {3 Agatean Empire ten-rhinu and 5 Agatean Empire rhinu}{5} - {4 Ankh-Morpork ten-dollars and 5 Ankh-Morpork dollars}{5} - {1 Lancre sovereign and 1 Lancre tencrown}{5} - {2 Ephebian minae}{5} - {1 Genuan ducat and 2 Genuan forins}{5} -} - +#VARIABLE {letter_types} { + {DLA}{DLA (close by\; smudged)} + {DLB}{DLB (close by\; handwriting)} + {DLC}{DLC (close by\; vague)} + {DLD}{DLD (close by\; city)} + {DLE}{DLE (far away\; smudged)} + {DLF}{DLF (far away\; handwriting)} + {DLG}{DLG (far away\; vague)} + {DLH}{DLH (far away\; city)} + {DLI}{DLI (difficult customer)} +}; #VARIABLE {missions[letter]} { {last_difficulty}{0} } +#nop Capture offering to deliver a letter; +#ALIAS {^offer to deliver {(d|D)[A-z]{2}}$} { + #send {%0}; + #format {missions[letter][last_difficulty]} {%u} {%1}; +}; - -#NOP ======================= -#NOP ===== Aliases ===== -#NOP ======================= +#nop Capture offering to return a letter; +#ALIAS {return letter} { + #send {return letter}; + #format {missions[letter][retry_list][$GMCP[charinfo][capname]]} {%T}; + /letter_returned; +}; +#ACTION {^You offer to give an? {.+?} letter to Frank.$} { + #format {missions[letter][retry_list][$GMCP[charinfo][capname]]} {%T}; + #nop [missions[letter][last_difficulty]]; + /letter_returned; +}; #ALIAS {/letter_returned} { #format {missions[letter][retry_list][$GMCP[charinfo][capname]]} {%T}; @@ -45,7 +40,7 @@ #line sub variable { #format {delayname} {letter_retry_%s} {$GMCP[charinfo][capname]}; #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now request another letter from Frank.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; + #format {letter_timer_line} {[missions] <138>*<900> %s can now request another letter from Frank.<099>} {$GMCP[charinfo][capname]}; #echo {$letter_timer_line}; #unvar {letter_timer_line}; @@ -55,127 +50,78 @@ #unvar {delayname}; }; -#ALIAS {^return letter$} { - #send { return letter}; - /letter_returned -}; +#nop Capture being rewarded for completing delivery of the letter; +#ACTION {^You have been awarded {[0-9]{1,7}} experience points for delivering this letter.$} { + #local {last_difficulty} {$missions[letter][last_difficulty]}; -#ALIAS {^give letter to {f|F}rank$} { - #send { give letter to Frank}; - /letter_returned -}; - - -#NOP ======================= -#NOP ===== Actions ===== -#NOP ======================= - -#ACTION {^You have been awarded %d experience points and given %2.$} { - #nop You have been awarded 60407 experience points and given 2 Agatean Empire ten-rhinu and 5 Agatean Empire rhinu. - - #var {missions[letter][last_difficulty]} {0}; - #if {&dead_letter_rewards[%2]} { - #var {missions[letter][last_difficulty]} {$dead_letter_rewards[%2]}; - }; -}; - - - -#ACTION {^You salute smartly as you deliver a letter} { - #nop If we don't have a difficulty match, we can't act. - #if {"$missions[letter][last_difficulty]" == "0"} { + #nop If we don't have a difficulty match, we can't act.; + #if {"$last_difficulty" == "0"} { #return; }; - #nop If we know the difficulty, set the appropriate timer; - #switch {$missions[letter][last_difficulty]} { - #case {1} { - #nop Local; - #if {&missions[letter][active_list][$GMCP[charinfo][capname]][local]} { #break; }; - #format {missions[letter][active_list][$GMCP[charinfo][capname]][local]} {%T}; - #line sub variable { - #format {delayname} {letter_local_%s} {$GMCP[charinfo][capname]}; - #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now do another local letter.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; - #echo {$letter_timer_line}; - #unvar {letter_timer_line}; + #nop You have been awarded 60407 experience points and given 2 Agatean Empire ten-rhinu and 5 Agatean Empire rhinu.; + #format {missions[letter][active_list][$GMCP[charinfo][capname]][$last_difficulty]} {%T}; - #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][local]}; - } {3600}; - }; - #unvar {delayname}; + #line sub variable { + #format {delayname} {letter_%s_%s} {$last_difficulty} {$GMCP[charinfo][capname]}; + #delay {$delayname} { + #format {letter_timer_line} {[missions] <138>*<900> %s can now do another "%c%s%c" letter.<099>} {$GMCP[charinfo][capname]} {Jade} {$letter_types[$last_difficulty]} {}; + #echo {$letter_timer_line}; + #unvar {letter_timer_line}; + + #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][$last_difficulty]}; + } {3600}; + }; + #unvar {delayname}; + #variable {missions[letter][last_difficulty]} {0}; +}; + +#ALIAS {/letters_recreate_delays} { + #format {current_time} {%T}; + + #nop == start with retry list; + #foreach {*missions[letter][retry_list][]} {player} { + #local {delay_time} {$missions[letter][retry_list][$player]}; + #math {delay_time} {600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[letter][retry_list][$player]}; + #continue; }; - #case {2} { - #nop Closeby; - #if {&missions[letter][active_list][$GMCP[charinfo][capname]][close]} { #break; }; - #format {missions[letter][active_list][$GMCP[charinfo][capname]][close]} {%T}; - #line sub variable { - #format {delayname} {letter_close_%s} {$GMCP[charinfo][capname]}; - #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now do another close-by letter.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; - #echo {$letter_timer_line}; - #unvar {letter_timer_line}; + #line sub variable { + #delay {$delay_time} { + #format {letter_timer_line} {[missions] <138>*<900> %s can now request another letter from Frank.<099>} {$player}; + #echo {$letter_timer_line}; + #unvar {letter_timer_line}; - #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][close]}; - } {3600}; + #unvar {missions[letter][retry_list][$player]}; + }; + }; + }; + #nop == do all the difficulties; + #foreach {*missions[letter][active_list][]} {player} { + #foreach {*missions[letter][active_list][$player][]} {$difficulty} { + #local {delay_time} {$missions[letter][active_list][$player][$difficulty]}; + #math {delay_time} {3600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[letter][active_list][$player][$difficulty]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {letter_timer_line} {[missions] <138>*<900> %s can now do another "%c%s%c" letter.<099>} {$player} {Jade} {$letter_types[$difficulty]} {}; + #echo {$letter_timer_line}; + #unvar {letter_timer_line}; + + #unvar {missions[letter][active_list][$player][$difficulty]}; + }; }; - #unvar {delayname}; - }; - #case {3} { - #nop Faraway; - #if {&missions[letter][active_list][$GMCP[charinfo][capname]][far]} { #break; }; - #format {missions[letter][active_list][$GMCP[charinfo][capname]][far]} {%T}; - - #line sub variable { - #format {delayname} {letter_far_%s} {$GMCP[charinfo][capname]}; - #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now do another far away letter.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; - #echo {$letter_timer_line}; - #unvar {letter_timer_line}; - - #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][far]}; - } {3600}; - }; - #unvar {delayname}; - }; - #case {4} { - #nop Distantland; - #if {&missions[letter][active_list][$GMCP[charinfo][capname]][distant]} { #break; }; - #format {missions[letter][active_list][$GMCP[charinfo][capname]][distant]} {%T}; - - #line sub variable { - #format {delayname} {letter_distant_%s} {$GMCP[charinfo][capname]}; - #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now do another distant land letter.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; - #echo {$letter_timer_line}; - #unvar {letter_timer_line}; - - #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][distant]}; - } {3600}; - }; - #unvar {delayname}; - }; - #case {5} { - #nop Difficultcustomer; - #if {&missions[letter][active_list][$GMCP[charinfo][capname]][difficult]} { #break; }; - #format {missions[letter][active_list][$GMCP[charinfo][capname]][difficult]} {%T}; - - #line sub variable { - #format {delayname} {letter_difficult_%s} {$GMCP[charinfo][capname]}; - #delay {$delayname} { - #format {letter_timer_line} {%c[MT] %c*%c %s can now do another difficult customer letter.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; - #echo {$letter_timer_line}; - #unvar {letter_timer_line}; - - #unvar {missions[letter][active_list][$GMCP[charinfo][capname]][difficult]}; - } {3600}; - }; - #unvar {delayname}; }; }; - #var {missions[letter][last_difficulty]} {0}; }; #CLASS {missiontimers_letters} {close}; diff --git a/src/missions/sailing.tin b/src/missions/sailing.tin new file mode 100644 index 0000000..9f9333d --- /dev/null +++ b/src/missions/sailing.tin @@ -0,0 +1,214 @@ +#CLASS {missiontimers_sailing} {kill}; +#CLASS {missiontimers_sailing} {open}; + +#VARIABLE {missions[sailing]} { + {experience}{0} + {active}{0} +}; + +#FUNCTION {display_timestamp} { + #math {min} {%1 / 60}; + #math {sec} {%1 % 60}; + + #format {result} {%%02d minutes and %%02d second(s)} {$min} {$sec}; + #return {$result}; +}; + +#NOP ==[Mission begins trigger]==; +#ACTION {^The loading of the ship complete, {Captain Smith|Chidder} wishes you a safe and profitable trip} { + #var {missions[sailing][experience]} {0}; + #var {missions[sailing][active]} {1}; + #format {missions[sailing][active_list][$GMCP[charinfo][capname]]} {%T}; + + #NOP == configure a message display when the timer is up ==; + #line sub variable { + #format {delayname} {sailing_%s} {$GMCP[charinfo][capname]}; + #delay {$delayname} { + #format {sailing_timer_line} {[missions] <138>*<900> %s can now do another sailing mission.<099>} {$GMCP[charinfo][capname]}; + #echo {$sailing_timer_line}; + #unvar {$sailing_timer_line}; + + #unvar {missions[sailing][active_list][$GMCP[charinfo][capname]]}; + } {7200}; + }; + #unvar {delayname}; +}; + +#NOP ==[Capture experience gains]==; +#ACTION {^As you {finish|complete} the {first|second|third} leg of your impossible voyage%*\(%d xp\)$} { + #math {missions[sailing][experience]} {$missions[sailing][experience] + %4}; +}; +#ACTION {^As the {kraken|serpent} sinks back beneath the waves, you {feel like the experience of confronting a terrifying sea monster has been enlightening|feel more experienced for having faced it and survived|feel your experiences settling into the back of your mind|think about the experiences you've had so far}. (%3 xp)$} { + #math {missions[sailing][experience]} {$missions[sailing][experience] + %3}; +}; + +#NOP ==[Final XP trigger]==; +#ACTION {^As you {finish|complete} the final leg of your impossible voyage%*\(%1 xp\)%*$} { + #var {missions[sailing][active]} {0}; + #format {current_time} {%T}; + #math {missions[sailing][experience]} {$missions[sailing][experience] + %1}; + + #math {missions[sailing][duration]} {$current_time - $missions[sailing][active_list][$GMCP[charinfo][capname]]}; + #showme {<169>[sail] Sailing Report: $missions[sailing][experience] xp earned in @display_timestamp{$missions[sailing][duration]}.<099>}; +}; + +#NOP ==[Group sailing report]== +#ALIAS {gssail} { + #if {$missions[sailing][experience] > 0} { + #send {group say [sail] Sailing Report: $missions[sailing][experience] xp earned in @display_timestamp{$missions[sailing][duration]}.}; + }; + #else { + #showme {<169>[sail] No sailing experience to report.<099>}; + }; +}; +#ALIAS {sailreport} { + #showme {<169>[sail] Sailing Report: $missions[sailing][experience] xp earned in @display_timestamp{$missions[sailing][duration]}.<099>}; +}; + +#NOP ==[Monster identification highlights]==; +#SUB {^A massive {kraken|sea serpent} crests} {A massive <168>%1<900> crests}; +#SUB {^The faint popping sounds of suction cups} {The faint popping sounds of <168>suction cups<900>}; +#SUB {^The grinding of scales against wood} {The grinding of <168>scales<900> against wood}; + +#NOP ==[Monster goes away]==; +#SUB {^As the {kraken|serpent} sinks back beneath the waves%2} {As the <168>%1<900> sinks back beneath the waves%2}; + +#NOP ==[Stage transitions]==; +#ACTION {^{The ship leaves the huge bank of fog as the weather calms.|The howling winds fade away as the weather calms.|The worst of the thunderstorm passes and the clouds overhead lighten and scatter to the winds as the weather calms.|The rain of hailstones slowly peters off and the clouds overhead lighten and scatter to the winds as the weather calms.|The last of the fog seeping in from overhead dissipates.|The sound of hailstones pounding overhead gradually peters out.|The sound of thunder overhead gradually peters out.|The faint howl of the wind overhead gradually peters out.}$} { + #showme {<168>[sail] ### Calm Stage Begins ###<099>}; +}; + +#ACTION {^{The previously calm weather fades away as grey clouds gather overhead and hailstones begin to rain from the sky.|The howling winds fade away as grey clouds gather overhead and hailstones begin to rain from the sky.|The worst of the thunderstorm passes as hailstones begin to rain from the sky.|The ship leaves the huge bank of fog as grey clouds gather overhead and hailstones begin to rain from the sky.|The sound of pounding hail starts to filter through from the deck above.|The sound of thunder overhead gradually peters out and is replaced by the sound of pounding hail.|The faint howl of the wind overhead gradually peters out and is replaced by the sound of pounding hail.|The last of the fog seeping in from overhead dissipates as the sound of pounding hail starts to filter through from the deck above.}$} { + #showme {<168>[sail] ### Hail Stage Begins ###<099>}; +}; + +#ACTION {^{The previously calm weather fades away as a rimwards gale starts building up, the sheer force of the wind pushing the ship backwards.|The worst of the thunderstorm passes and the clouds overhead lighten and scatter to the winds just as a rimwards gale starts building up, the sheer force of the wind pushing the ship backwards.|The rain of hailstones slowly peters off and the clouds overhead lighten and scatter to the winds just as a rimwards gale starts building up, the sheer force of the wind pushing the ship backwards.|The ship leaves the huge bank of fog as a rimwards gale starts building up, the sheer force of the wind pushing the ship backwards.|The faint howling of what must be quite a strong wind begins to filter through the deck above.|The last of the fog seeping in from overhead dissipates as the faint howling of what must be quite a strong wind begins to filter through the deck above.|The sound of hailstones pounding overhead gradually peters out and is replaced by the faint howling of what must be quite a strong wind.|The sound of thunder overhead gradually peters out and is replaced by the faint howling of what must be quite a strong wind.}$} { + #showme {<168>[sail] ### Gale Stage Begins ###<099>}; +}; + +#ACTION {^{The previously calm weather fades away as a growing wind sweeps in dark clouds from all directions, lightning flashes and thunder booms, the beginning of a thunderstorm.|The ship leaves the huge bank of fog as a growing wind sweeps in dark clouds from all directions, lightning flashes and thunder booms, the beginning of a thunderstorm.|The howling winds fade away as a growing wind sweeps in dark clouds from all directions, lightning flashes and thunder booms, the beginning of a thunderstorm.|The rain of hailstones slowly peters off as lightning flashes and thunder booms, the beginning of a thunderstorm.|The rumble of thunder comes through from the deck above.|The sound of hailstones pounding overhead gradually peters out and is replaced by the rumble of thunder.|The last of the fog seeping in from overhead dissipates as the rumble of thunder comes through from the deck above.|The faint howl of the wind overhead gradually peters out and is replaced by the rumble of thunder.}$} { + #showme {<168>[sail] ### Storm Stage Begins ###<099>}; +}; + +#ACTION {^{The previously calm weather fades away as the ship enters a huge bank of fog.|The rain of hailstones slowly peters off and the clouds overhead lighten and scatter to the winds just as the ship enters a huge bank of fog.|The worst of the thunderstorm passes and the clouds overhead lighten and scatter to the winds just as the ship enters a huge bank of fog.|The howling winds fade away as the ship enters a huge bank of fog.|Wisps of fog begin to drift in through tiny gaps in the deck above.|The sound of hailstones pounding overhead gradually peters out as wisps of fog begin to drift in through tiny gaps in the deck above.|The sound of thunder overhead gradually peters out as wisps of fog begin to drift in through tiny gaps in the deck above.|The faint howl of the wind overhead gradually peters out as wisps of fog begin to drift in through tiny gaps in the deck above.}$} { + #showme {<168>[sail] ### Fog Stage Begins ###<099>}; +}; + +#NOP ==[Ship starts/stops moving]==; +#HIGHLIGHT {^{Steam whistles from the smokestack as the ship begins to move|You feel the ship begin to move}%*} {Cyan underscore}; +#HIGHLIGHT {^{You feel the ship slow to a halt.|The steam from the smokestack dwindles to nothing as the ship slows to a halt.}} {Cyan underscore}; + +#NOP ==[Overboard drag messages]==; +#SUB {^The ship steams off, dragging you along by the rope tied around your waist.} {The ship steams off, dragging you along by the <128>rope<900> tied around your waist.}; +#SUB {^The ship steams off and you hurriedly swim along to keep up with it.} {The ship steams off and you hurriedly <118>swim along<900> to keep up with it.}; + +#NOP ==[Monster phase highlights - Serpent]==; +#SUB {^The sea serpent {reaches for|lunges towards|strikes forward swiftly at} you from %2. Run!} {<118>The sea serpent %1 you from %2<118>. <488>Run<088><118>!}; +#SUB {^%1, the sea serpent {reaches for|lunges towards|strikes forward swiftly at} you from %3. Run!} {<118>%1, the sea serpent %2 you from %3<118>. <488>Run<088><118>!}; +#HIGHLIGHT {^The sea serpent {reaches for|lunges towards|strikes forward swiftly at} %1.$} {Blue}; +#HIGHLIGHT {^%1, the sea serpent {reaches for|lunges towards|strikes forward swiftly at} %2.$} {Blue}; +#HIGHLIGHT {^The sea serpent aborts its strike and draws its head back, looking for its missing prey.} {light green}; +#HIGHLIGHT {^The sea serpent's venomous fangs gouge long scratches in the deck as its strike misses.} {light green}; +#HIGHLIGHT {^The sea serpent chomps down on nothing but air, its prey having escaped.} {light green}; + +#NOP ==[Monster phase highlights - Kraken]==; +#SUB {^{Quick as lightning|Striking like a cobra|In the blink of an eye|With a blur of speed|Moving too fast to see}, the tentacle snakes around %2 legs and coils its way up to {your|his|her|its} torso, binding {you|him|her|it} in an unescapable embrace.} {<118>%1, the tentacle snakes around %2<118> legs and coils its way up to %3 torso, binding %4 in an unescapable embrace.}; +#HIGHLIGHT {^%1 swing{s?} {your|his|her|its} %2at the tentacle wrapped around %2, {cutting|scoring} a {shallow|small|light} scratch into its rubbery skin.} {Blue}; +#HIGHLIGHT {^The tentacle wrapped around %1 unwraps itself and {slips|retreats|retracts} back into the sea.} {White}; +#HIGHLIGHT {^The tentacle coils tighter, making %1 go blue in the face.} {Red}; +#HIGHLIGHT {^Limbs bound tightly by the kraken's tentacle, %1 struggles to move but gets nowhere.} {yellow}; +#HIGHLIGHT {^%1 looks pained as the tentacle slowly constricts the life out of {her|him|it}.} {Red}; +#HIGHLIGHT {^You feel your bones creaking as the tentacle slowly constricts the life out of you.} {Red}; + +#NOP ==[Fire related highlights]==; +#SUB {^Long shadows dance as flickering firelight from %1 {blazes into|illuminates|casts light on} the room.} {Long shadows dance as flickering firelight from <419>%1<900> %2 the room.}; +#SUB {^Flickering firelight can be seen to %1{\.$|, }} {Flickering firelight can be seen to <119>%1<900>%2}; + +#HIGHLIGHT {^A small fire has started here.} {Orange}; +#HIGHLIGHT {^Several small blazes are eagerly licking at the floorboards.} {Orange}; +#HIGHLIGHT {^The room is filled with a huge conflagration, sparks spilling out into the nearby rooms.} {Orange}; +#HIGHLIGHT {^Fire fills the room, burning merrily without regard for the structural integrity of the ship.} {Orange}; + +#NOP ==[Ice and Seaweed highlights]==; +#HIGHLIGHT {%1. It sounds like ice might be forming on the hull!$} {}; +#HIGHLIGHT {%*ice has formed%*$} {}; +#HIGHLIGHT {%*slippery layer of ice%*$} {}; +#HIGHLIGHT {^The dire seaweed clinging onto the hull glows faintly, %1 its way {through|into} the {hull|wood|ship}.$} {Jade}; +#HIGHLIGHT {^The ship plows through a field of floating dire seaweed, some of which glows green and latches onto the hull aggressively!$} {Jade}; + +#NOP ==[Wrangling job highlights]==; +#SUB {According to the dials on the side, it seems to be %1 and %2\.} {According to the dials on the side, it seems to be <039>%1<900> and <039>%2<900>\.}; +#HIGHLIGHT {^%*A little bell on the MK I Boiling Engine rings to indicate that it's out of water.%*$} {light red underscore}; +#HIGHLIGHT {^%*dragon looks up at %* accidentally spraying flame over the floorboards\.$} {light red underscore}; +#HIGHLIGHT {^%*swamp dragon leaps upon the squeaky toy animal and spends an enjoyable three and a half minutes tearing it to squeaking shreds. Finally, it climbs atop the pile of rubber scraps and sits there triumphantly.%*$} {light green}; +#HIGHLIGHT {^%*You throw the ball a few more times, getting a little further each time to keep %* swamp dragon entertained, until a particularly hard throw sends it bouncing right off the ship. Oh well.$} {light green}; +#HIGHLIGHT {very {bored|hungry}} {red}; +#HIGHLIGHT {somewhat {bored|hungry}} {orange}; +#HIGHLIGHT {a little {bored|hungry}} {yellow}; +#HIGHLIGHT {not at all {bored|hungry}} {green}; +#HIGHLIGHT {^%*finishe%. off the last of the%*$} {green}; +#HIGHLIGHT {^%*where it sits down and starts breathing fire onto the Boiling Engine.} {green}; + +#NOP ==[Deckhanding job highlights]==; +#HIGHLIGHT {You manage to shatter all the reamining sea ice on the hull.} {Cyan}; +#HIGHLIGHT {You manage to hack off all the reamining dire seaweed from the hull.} {Jade}; +#HIGHLIGHT {^You feel too tired to tie any knots at the moment.$} {red}; +#HIGHLIGHT {^You tie %1 of rope securely in a %2 knot around %3 waist.$} {Yellow}; +#HIGHLIGHT {^Under the strain, your rope gives up the ghost and unravels completely into wispy threads that float away in the wind.$} {light red underscore}; +#HIGHLIGHT {^You tie %1 cargo crates down securely %* with {the coil|one of the coils} of rope, preventing {them|it} from sliding.} {light green}; +#SUB {^You manage to {shatter|hack off} some of the {sea ice on|dire seaweed from} the hull.$} {You manage to %1 some of the<900> %2 the hull.}; +#SUB {^You manage to {shatter|hack off} all the remaining {sea ice on|dire seaweed from} the hull.$} {You manage to %1 all the remaining<900> %2 the hull.}; +#HIGHLIGHT { colossal amount of sea ice} {red}; +#HIGHLIGHT { thick mass of sea ice} {red}; +#HIGHLIGHT { thin layer of sea ice} {yellow}; +#HIGHLIGHT { few patches of sea ice} {yellow}; +#HIGHLIGHT { badly cracked} {red}; +#HIGHLIGHT { rather dented} {yellow}; +#HIGHLIGHT { a little scuffed up} {yellow}; +#HIGHLIGHT { in perfect condition} {green}; +#HIGHLIGHT { hanging on by a thread} {red}; +#HIGHLIGHT { very frayed} {red}; +#HIGHLIGHT { somewhat frayed} {yellow}; +#HIGHLIGHT { a little frayed} {yellow}; + +#NOP ==[Helming job highlights]==; +#HIGHLIGHT {^Caught in a strong backwards current, the ship shudders intensely as it zooms back a few miles.$} {light orange}; +#HIGHLIGHT {^Caught in a strong forwards current, the ship vibrates intensely as it zooms ahead a few miles.$} {light green}; +#HIGHLIGHT {The ship abruptly starts spinning in circles. After a few rounds, it manages to escape the whirlpool, heading in a different direction from before.} {Yellow underscore}; +#SUB {^The impact knocks the ship slightly off course to %1.$} {The impact knocks the ship slightly off course to <488>%1<900>.}; +#SUB {^The powerful headwind causes the ship to drift off course towards %1.$} {The powerful headwind causes the ship to drift off course towards <488>%1<900>.}; +#SUB {^A powerful gust of wind turns the ship toward %1, as if deliberately mocking all attempts to hold a steady course.$} {A powerful gust of wind turns the ship toward <488>%1<900>, as if deliberately mocking all attempts to hold a steady course.}; +#SUB {^Despite your crew's best efforts, the gusts of wind blow the ship off course in the %1 direction.$} {Despite your crew's best efforts, the gusts of wind blow the ship off course in the <488>%1<900> direction.}; + +#NOP ==[Helming directionality or compass highlights]==; +#SUB {^From the compass, you can see that the ship is heading %1.} {From the compass, you can see that the ship is heading <868>%1<099>.}; +#HIGHLIGHT {^You gaze up at the stars, but are too tired to figure out anything useful.$} {Red}; +#HIGHLIGHT {^You gaze up at the stars, but can't figure out anything from them.$} {Red underscore}; +#SUB {^{Gazing up at the stars|Noting the positions of some key constellations|Reading the stars like a chart|Squinting up at the sun|Carefully looking up out of the corner of your eye|From a careful squint at the sun}, you {are confident|decide|determine|are able to ascertain|decide from the sun's position|determine from the sun's position|guess} that the ship is {currently heading|heading|heading directly} {hubwards|turnwise-hubwards|widdershins-hubwards|rimwards|turnwise-rimwards|widdershins-rimwards|turnwise|widdershins}%*} {<058>%1, you %2 that the ship is %3 <168>%4<058>%5}; +#SUB {^{Gazing up at the stars|Noting the positions of some key constellations|Reading the stars like a chart|Squinting up at the sun|Carefully looking up out of the corner of your eye|From a careful squint at the sun}, you {are confident|decide|determine|are able to ascertain|decide from the sun's position|determine from the sun's position|guess} that the ship must be {currently heading|heading|heading directly} {hubwards|turnwise-hubwards|widdershins-hubwards|rimwards|turnwise-rimwards|widdershins-rimwards|turnwise|widdershins}%*} {<058>%1, you %2 that the ship must be %3 <138>%4<058>%5}; + +#ALIAS {/sailing_recreate_delays} { + #format {current_time} {%T}; + + #foreach {*missions[sailing][active_list][]} {player} { + #local {delay_time} {$missions[sailing][active_list][$player]}; + #math {delay_time} {7200 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[sailing][active_list][$player]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {sailing_timer_line} {[missions] <138>*<900> %s can now do another sailing mission.<099>} {$player}; + #echo {$sailing_timer_line}; + #unvar {sailing_timer_line}; + + #unvar {missions[sailing][active_list][$player]}; + }; + }; + }; +}; + +#CLASS {missiontimers_sailing} {close}; diff --git a/src/missions/sausages.tin b/src/missions/sausages.tin index 68c7dee..56dce02 100644 --- a/src/missions/sausages.tin +++ b/src/missions/sausages.tin @@ -42,7 +42,7 @@ #line sub variable { #delay {600} { - #format {sausage_retry_line} {%c[MT] %c*%c %s%c can now ask for a new sausage mission from Sam Slager.} {bold blue} {bold yellow} {bold green}{$GMCP[charinfo][capname]} {bold blue}; + #format {sausage_retry_line} {[missions] <138>*<900> %scan now ask for a new sausage mission from Sam Slager.<099>} {$GMCP[charinfo][capname]}; #echo {$sausage_retry_line}; #unvar {sausage_retry_line}; @@ -61,7 +61,7 @@ #line sub variable { #format {delayname} {sausage_easy_%s} {$GMCP[charinfo][capname]}; #delay {$delayname} { - #format {sausage_timer_line} {%c[MT] %c*%c %s can now do another unspiced sausage mission.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; + #format {sausage_timer_line} {[missions] <138>*<900> %s can now do another unspiced sausage mission.<099>} {$GMCP[charinfo][capname]}; #echo {$sausage_timer_line}; #unvar {sausage_timer_line}; @@ -76,7 +76,7 @@ #line sub variable { #format {delayname} {sausage_hard_%s} {$GMCP[charinfo][capname]}; #delay {$delayname} { - #format {sausage_timer_line} {%c[MT] %c*%c %s can now do another spiced sausage mission.} {bold blue} {bold yellow} {bold blue} {$GMCP[charinfo][capname]}; + #format {sausage_timer_line} {[missions] <138>*<900> %s can now do another spiced sausage mission.<099>} {$GMCP[charinfo][capname]}; #echo {$sausage_timer_line}; #unvar {sausage_timer_line}; @@ -88,6 +88,72 @@ }; }; +#ALIAS {/sausage_recreate_delays} { + #format {current_time} {%T}; + + #nop == start with retry list; + #foreach {*missions[sausage][retry_list][]} {player} { + #local {delay_time} {$missions[sausage][retry_list][$player]}; + #math {delay_time} {600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[sausage][retry_list][$player]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {sausage_retry_line} {[missions] <138>*<900> %s can now ask for a new sausage mission from Sam Slager.<099>} {$player}; + #echo {$sausage_retry_line}; + #unvar {sausage_retry_line}; + + #unvar {missions[sausage][retry_list][$player]}; + }; + }; + }; + #nop == easy and hard missions; + #foreach {*missions[sausage][active_list][]} {player} { + #if {&missions[sausage][active_list][$player][easy]} { + #local {delay_time} {$missions[sausage][active_list][$player][easy]}; + #math {delay_time} {3600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[sausage][active_list][$player][easy]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {sausage_timer_line} {[missions] <138>*<900> %s can now do another unspiced sausage mission.<099>} {$player}; + #echo {$sausage_timer_line}; + #unvar {sausage_timer_line}; + + #unvar {missions[sausage][active_list][$player][easy]}; + }; + }; + }; + #if {&missions[sausage][active_list][$player][hard]} { + #local {delay_time} {$missions[sausage][active_list][$player][hard]}; + #math {delay_time} {3600 - ($current_time - $delay_time)}; + + #if {$delay_time < 1} { + #unvar {missions[sausage][active_list][$player][hard]}; + #continue; + }; + + #line sub variable { + #delay {$delay_time} { + #format {sausage_timer_line} {[missions] <138>*<900> %s can now do another unspiced sausage mission.<099>} {$player}; + #echo {$sausage_timer_line}; + #unvar {sausage_timer_line}; + + #unvar {missions[sausage][active_list][$player][hard]}; + }; + }; + }; + }; +}; + #ALIAS {/sausage_mission_prep} { #action {You have four hours to deliver %%1 fresh %%2 sausages to Sam Slager.$} { /sausage_mission_start; diff --git a/src/personal.tin b/src/personal.tin new file mode 100644 index 0000000..dffb6f9 --- /dev/null +++ b/src/personal.tin @@ -0,0 +1,8 @@ +#class {personal} {kill}; +#class {personal} {open}; + +#alias {/personal.gmcp.writtenmap %1} { + #nop empty shell; +}; + +#class {personal} {close}; diff --git a/src/prompt.tin b/src/prompt.tin index 709c5c1..486da73 100644 --- a/src/prompt.tin +++ b/src/prompt.tin @@ -1,38 +1,56 @@ +#class prompt kill; +#class prompt open; + #NOP == Prompt Update Function == #nop colors #function {tpa_col} { - #if {"%0" == "invisible"} + #if {"%0" == "invisible"} { #return <178>; - #if {"%0" == "dull red"} + }; + #if {"%0" == "dull red"} { #return <018>; - #if {"%0" == "bright red"} + }; + #if {"%0" == "bright red"} { #return <118>; - #if {"%0" == "wobbling orange"} + }; + #if {"%0" == "wobbling orange"} { #return <188>; - #if {"%0" == "flickering yellow"} + }; + #if {"%0" == "flickering yellow"} { #return <138>; - #return; + }; + #return <000>; } - #ALIAS {update_prompt} { #nop XP monitor details; xp_monitor; - #format {prompt_xp} {%Gxp in %s (%Gk/h)} {$session_xp} {$uptime[display]} {$xprate}; + + #format {prompt_xp} {%Gxp in %s (%Gk/h)} {$window_xp} {$uptime[window][display]} {$xprate}; + #format {prompt_xp} {%-40s} {$prompt_xp}; #nop TPA monitor details; #var {prompt_tpa} {}; - #if {$tpa_status == 1} { - #format {prompt_tpa} {%cTPA%c} {@tpa_col{$tpa_color}} {<098>}; - }; + #format {prompt_tpa} {%cTPA%c} {@tpa_col{$tpa_color}} {<098>}; #nop Quota monitor details; #var {prompt_quota} {}; - #if {"a} { - #format {prompt_quota} {Quota: %s} {@quota_time_left{}}; + #if {&{quota}} { + #format {prompt_quota} {Quota: %-8s} {@quota_time_left{}}; }; - #format {prompt_line} {%+5s %+40s %+15s} {$prompt_tpa} {$prompt_xp} {$prompt_quota}; - #showme {$prompt_line} {-1}; -} + #nop #format {prompt_line} {%+5s %+40s %+15s} {$prompt_tpa} {$prompt_xp} {$prompt_quota}; + #nop #showme {$prompt_line} {2} {3}; + #showme {$prompt_tpa} {2} {3}; + #showme {$prompt_xp} {2} {10}; + #showme {$prompt_quota} {2} {48}; + #nop Write to line 2, and start 3 columns in so we don't disrupt the box drawn; + + #nop Put the time with seconds to the right hand side; + #format {clock} {%t} {%I:%M:%S %p}; + #showme {$clock} {2} {-13}; +}; + + +#class prompt close; diff --git a/src/quota.tin b/src/quota.tin index 753ef02..ffef771 100644 --- a/src/quota.tin +++ b/src/quota.tin @@ -87,7 +87,7 @@ #var {quota[reciepts]} {0}; #nop Quota period is over, announce it.; - #format {quota_display_line} {%c[Quota] %c*%c %s's quota has %creset%c.<099>} {bold blue} {bold yellow} {bold blue} {$quota[who]} {bold red} {bold blue}; + #format {quota_display_line} {[quota] <138>* %s's quota has <118>reset.<099>} {$quota[who]}; #showme {$quota_display_line}; }; @@ -95,19 +95,19 @@ #if {"$quota[alert]" == "on"} { #nop Message an hour before; #if {$quota[time][hour] == 0 && $quota[time][min] == 59 && $quota[time][sec] == 59} { - #format {quota_display_line} {%c[Quota] %c*%c %s's quota flips over in %cone hour%c.<099>} {bold blue} {bold yellow} {bold blue} {$quota[who]} {bold red} {bold blue}; + #format {quota_display_line} {[quota] <138>* %s's quota flips over in <148>one hour.<099>} {$quota[who]}; #echo {$quota_display_line \a}; }; #nop Message 30 minutes before; #if {$quota[time][hour] == 0 && $quota[time][min] == 29 && $quota[time][sec] == 59} { - #format {quota_display_line} {%c[Quota] %c*%c %s's quota flips over in %chalf an hour%c.<099>} {bold blue} {bold yellow} {bold blue} {$quota[who]} {bold red} {bold blue}; + #format {quota_display_line} {[quota] <138>* %s's quota flips over in <148>half an hour.<099>} {$quota[who]}; #echo {$quota_display_line \a}; }; #nop Message 10 minutes before; #if {$quota[time][hour] == 0 && $quota[time][min] == 9 && $quota[time][sec] == 59} { - #format {quota_display_line} {%c[Quota] %c*%c %s's quota flips over in %cten minutes%c.<099>} {bold blue} {bold yellow} {bold blue} {$quota[who]} {bold red} {bold blue}; + #format {quota_display_line} {[quota] <138>* %s's quota flips over in <118>ten minutes.<099>} {$quota[who]}; #echo {$quota_display_line \a}; }; }; diff --git a/src/quow.db b/src/quow.db new file mode 100644 index 0000000..f8d58a7 Binary files /dev/null and b/src/quow.db differ diff --git a/src/spottimers.tin b/src/spottimers.tin index 8d1b955..8229323 100644 --- a/src/spottimers.tin +++ b/src/spottimers.tin @@ -2,265 +2,397 @@ #CLASS {xptimers} {open}; #VARIABLE {xptimer_file} {logs/xptimers.log}; +#VARIABLE {xptimersync_file} {logs/features/xptimers_sync.tin}; #VARIABLE {killtimers} { {Delbert}{ {time}{0} {name}{delbert} {group}{false} - {respawn}{30} + {respawn}{40} } {the Triad boss}{ {time}{0} {name}{medina boss} {group}{false} - {respawn}{30} + {respawn}{40} } {the bandit leader}{ {time}{0} - {name}{bandit leader} + {name}{bandits} {group}{false} - {respawn}{30} + {respawn}{40} } {Hlakket the Bartender}{ {time}{0} {name}{hlakket} {group}{false} - {respawn}{30} + {respawn}{40} } {the Zoon Liar}{ {time}{0} {name}{zoon liar} {group}{false} - {respawn}{30} + {respawn}{40} } {Slim Stevie}{ {time}{0} {name}{rogues} {group}{false} - {respawn}{30} + {respawn}{40} } {the %* grflx worker}{ {time}{0} {name}{grflx} {group}{false} - {respawn}{30} + {respawn}{40} } {the %* student}{ {time}{0} {name}{dojo} {group}{false} - {respawn}{30} + {respawn}{40} } {the ceremonial guard}{ {time}{0} {name}{cguards} {group}{false} - {respawn}{30} + {respawn}{40} } {the smuggler captain}{ {time}{0} - {name}{smug captain} + {name}{smug cap} {group}{false} - {respawn}{30} + {respawn}{40} } {the giant leader}{ {time}{0} {name}{giants} {group}{false} - {respawn}{30} + {respawn}{40} } {the %* rujona}{ {time}{0} {name}{snail} {group}{true} - {respawn}{30} + {respawn}{40} } {the %* nitsuni}{ {time}{0} {name}{snail} {group}{true} - {respawn}{30} + {respawn}{40} } {the Ome outlaw}{ {time}{0} {name}{snail} {group}{true} - {respawn}{30} + {respawn}{40} } {the %* lion}{ {time}{0} - {name}{shaker lions} + {name}{shaker} {group}{false} - {respawn}{30} + {respawn}{40} + } + {the %* crocodile}{ + {time}{0} + {name}{offler} + {group}{false} + {respawn}{40} } {Louis Accardo}{ {time}{0} {name}{stables} {group}{true} - {respawn}{30} + {respawn}{40} } {Samuel Casso}{ {time}{0} {name}{stables} {group}{true} - {respawn}{30} + {respawn}{40} } {Enrico Persuica}{ {time}{0} {name}{stables} {group}{true} - {respawn}{30} + {respawn}{40} } {Joe Corrola}{ {time}{0} {name}{stables} {group}{true} - {respawn}{30} + {respawn}{40} } {Marlon Gumboni}{ {time}{0} {name}{casino} {group}{true} - {respawn}{30} + {respawn}{40} } {Frankie Harvard}{ {time}{0} {name}{casino} {group}{true} - {respawn}{30} + {respawn}{40} } {Geraldo Ciaco}{ {time}{0} {name}{casino} {group}{true} - {respawn}{30} + {respawn}{40} } {Charles Marchella}{ {time}{0} {name}{casino} {group}{true} - {respawn}{30} + {respawn}{40} } -} + {the %* hermit}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the %* patroller}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the {strict|serious|grim|tough|stern} captain}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the %* desert nomad}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the peacekeeper commander}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the %* mystic}{ + {time}{0} + {name}{oasis} + {group}{true} + {respawn}{40} + } + {the {Tang|Fang|Sung|Hong|McSweeney} captain}{ + {time}{0} + {name}{bmarket} + {group}{true} + {respawn}{40} + } + {the citadel guard}{ + {time}{0} + {name}{citguard} + {group}{false} + {respawn}{40} + } +}; #VARIABLE {visittimers} { {AMShades}{ {time}{0} {name}{shades} {group}{false} - {respawn}{12} + {respawn}{20} } {ebff897af2b8bb6800a9a8636143099d0714be07}{ {time}{0} {name}{smugglers} {group}{false} - {respawn}{12} + {respawn}{20} } {70546ec71867645ab5c51e9ce6087b75dcf4176f}{ {time}{0} {name}{offler} {group}{false} - {respawn}{30} + {respawn}{40} + } + {2950a174f65ef8e38eee444295c9b4832d36ab60}{ + {time}{0} + {name}{dragon} + {group}{false} + {respawn}{40} } {f6c60dd36ed38707eb954c3a670f2f9370a79399}{ {time}{0} - {name}{black market} + {name}{bmarket} {group}{true} - {respawn}{30} + {respawn}{40} } {e4646eab906aef1187a9dd3e980441b8f6b8d68c}{ {time}{0} - {name}{black market} + {name}{bmarket} {group}{true} - {respawn}{30} + {respawn}{40} } {0ae9d8c4f2b48f4fcc4cb57fd85443399be2ecc5}{ {time}{0} - {name}{black market} + {name}{bmarket} {group}{true} - {respawn}{30} + {respawn}{40} } {c7c6f5720d2c587f80696b6f0ec7f9ac0f084b06}{ {time}{0} - {name}{black market} + {name}{bmarket} {group}{true} - {respawn}{30} + {respawn}{40} } {3862c5fd0fe83f4080bdb9b94519bf8da89d5015}{ {time}{0} {name}{parades} {group}{true} - {respawn}{30} + {respawn}{40} } {cca9645565f2bb0d017ea9b1dcc9d1b3e9fcfe72}{ {time}{0} {name}{cabbage} {group}{false} - {respawn}{30} + {respawn}{40} } -} + {7377f4483763eca72e0b97a87f71c5266e33f2cd}{ + {time}{0} + {name}{cocoons} + {group}{false} + {respawn}{40} + } +}; +#nop Set this to 1 when we record first spot, dont overwrite file until set; +#VARIABLE {xptimer_recent_flag} {0}; + +#ALIAS {dt help} { + #showme {[spottimers] Hotspot death/visit timers help:<099>}; + #showme {Hotspot timers record how long since you've killed (death) an NPC or last entered (visit) a particular room.<099>}; + #showme {}; + #showme { * Command<099>: "dt<099>"<099>}; + #showme {\t<099>List all current timers<099>}; + #showme { * Command<099>: "dt help<099>"<099>}; + #showme {\t<099>Display this helpfile<099>}; + #showme { * Command<099>: "dtreset all<099>"<099>}; + #showme {\t<099>Reset all timers to unseen, use after idling to clear stale timers<099>}; + #showme { * Command<099>: "dtreset <099>"<099>}; + #showme {\t<099>Set a spot as if you had just killed/visted, e.g. "dtreset zoon liar<099>"<099>}; + #showme { * Command<099>: "gsdt<099>"<099>}; + #showme {\t<099>Send all current timers to "group say<099>" to share<099>}; + #showme { * Command<099>: "dtsync <099>"<099>}; + #showme {\t<099>Send a tell to a player using this script to sync your timers to them<099>}; + #showme { * Command<099>: "dtsave<099>"<099>}; + #showme {\t<099>Force save of current timers to file, use before restarting client<099>}; + #showme { * Command<099>: "dtload<099>"<099>}; + #showme {\t<099>Load spot timers from file, use after restarting client<099>}; + #showme {}; + #showme {<099>Timers are saved to file automatically every five minutes. Output similar to "dt" is saved to "logs/xptimers.log", used to output in another window.<099>}; + #showme {}; +}; + #ALIAS {dtreset} { + #variable {dtreset_all} {0}; #regex {%1} {all} { - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #format {killtimers[$spot][time]} {0}; - } - #foreach {$visittimers[]} {spot} { + }; + #foreach {*visittimers[]} {spot} { #format {visittimers[$spot][time]} {0}; - } - #format {xptimer_message} {%c[XPTimer] %c*%c Resetting all spots to Unseen.} {bold blue} {bold yellow} {bold blue}; + }; + #format {xptimer_message} {[hotspots] <138>* Resetting all spots to Unseen.<099>}; #echo {$xptimer_message}; #variable {dtreset_all} {1}; + #variable {xptimer_recent_flag} {0}; }; #if {$dtreset_all < 1} { - #foreach {$killtimers[]} {spot} { + #list {spot_groups} {clear}; + #foreach {*killtimers[]} {spot} { + #var {xptimer_message} {}; #regex {%1 %2} {$killtimers[$spot][name]} { #format {killtimers[$spot][time]} {%T}; #if {"$killtimers[$spot][group]" == "true"} { - #format {xptimer_message} {%c[XPTimer] %c*%c Resetting death timer for%c $killtimers[$spot][name] ($spot)%c.} {bold blue} {bold yellow} {bold blue} {bold green} {bold blue}; + #list {spot_groups} {find} {$killtimers[$spot][name]} {group_exists}; + #if {"$group_exists" == "0"} { + #list {spot_groups} {add} {$killtimers[$spot][name]}; + #format {xptimer_message} {[hotspots] <138>* Resetting death timer for%c $killtimers[$spot][name]%c.<099>} {Green} {}; + }; }; #else { - #format {xptimer_message} {%c[XPTimer] %c*%c Resetting death timer for%c $killtimers[$spot][name]%c.} {bold blue} {bold yellow} {bold blue} {bold green} {bold blue}; + #format {xptimer_message} {[hotspots] <138>* Resetting death timer for%c $killtimers[$spot][name]%c.<099>} {Green} {}; + }; + + #format {xp_message_check} {%L} {$xptimer_message}; + #if {$xp_message_check > 0} { + #echo {$xptimer_message}; + #variable {xptimer_recent_flag} {1}; }; - #echo {$xptimer_message}; }; - } - #foreach {$visittimers[]} {spot} { + }; + #list {spot_groups} {clear}; + #foreach {*visittimers[]} {spot} { + #var {xptimer_message} {}; #regex {%1 %2} {$visittimers[$spot][name]} { #format {visittimers[$spot][time]} {%T}; #if {"$visittimers[$spot][group]" == "true"} { - #format {xptimer_message} {%c[XPTimer] %c*%c Resetting visit timer for%c $visittimers[$spot][name] ($spot)%c.} {bold blue} {bold yellow} {bold blue} {bold green} {bold blue}; + #list {spot_groups} {find} {$visittimers[$spot][name]} {group_exists}; + #if {"$group_exists" == "0"} { + #list {spot_groups} {add} {$visittimers[$spot][name]}; + #format {xptimer_message} {[hotspots] <138>* Resetting visit timer for%c $visittimers[$spot][name]%c.<099>} {Green} {}; + }; }; #else { - #format {xptimer_message} {%c[XPTimer] %c*%c Resetting visit timer for%c $visittimers[$spot][name]%c.} {bold blue} {bold yellow} {bold blue} {bold green} {bold blue}; + #format {xptimer_message} {[hotspots] <138>* Resetting visit timer for%c $visittimers[$spot][name]%c.<099>} {Green} {}; + }; + + #format {xp_message_check} {%L} {$xptimer_message}; + #if {$xp_message_check > 0} { + #echo {$xptimer_message}; + #variable {xptimer_recent_flag} {1}; }; - #echo {$xptimer_message}; }; - } - } + }; + }; #unvariable {dtreset_all}; -} +}; #ACTION {%1 deals the death blow to %2.} { - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #regex {%2} {$spot} { #format {killtimers[$spot][time]} {%T}; + #variable {xptimer_recent_flag} {1}; }; - } -} + }; +}; #ACTION {You kill %1.} { - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #regex {%1} {$spot} { #format {killtimers[$spot][time]} {%T}; + #variable {xptimer_recent_flag} {1}; }; - } -} + }; +}; + +#nop The smuggler captain dies. +#ACTION {^%1 dies.$} { + #foreach {*killtimers[]} {spot} { + #nop Force case insensitivity on the match due to "The" "A" being capitalized; + #regex {%1} {%i$spot} { + #format {killtimers[$spot][time]} {%T}; + #variable {xptimer_recent_flag} {1}; + }; + }; +}; #FUNCTION {write_spot} { #var {spotname} {%1}; @@ -276,19 +408,32 @@ #math {mins} {$current_time - $spottime}; #math {mins} {($mins - (60 - 1)) / 60}; - #nop Choose a color-code based on downtime; - #if {$mins > ($spotrespawn + 40)} { - #var {current_spot_color} {<099>}; - }; - #elseif {$mins > ($spotrespawn + 20)} { - #var {current_spot_color} {<119>}; - }; - #elseif {$mins > $spotrespawn} { - #var {current_spot_color} {<139>}; + #nop 2/2026 changing scheme here; + #nop if rat is 40, 30-45 yellow, 45-60 red, beyond 60 miss; + #math {low_b} {$spotrespawn - 10}; + #math {up_b} {$spotrespawn + 5}; + #math {miss_b} {$spotrespawn + 20}; + + #nop if we're at or beyond the lower bound; + #if {$mins >= $low_b} { + #nop are we at or below upper bound? YELLOW; + #if {$mins <= $up_b} { + #var {current_spot_color} {<139>}; + }; + + #nop are we beyond the miss zone? show grey; + #elseif {$mins > $miss_b} { + #var {current_spot_color} {<099>}; + }; + + #nop otherwise we're in sweet spot, RED; + #else { + #var {current_spot_color} {<119>}; + }; }; #else { #var {current_spot_color} {<099>}; - } + }; #if {$mins > 99} { #var {mins} {99} }; @@ -299,11 +444,11 @@ #else { #nop Zero pad under 10 mins; #format {spotstring} { - (0%dm) %c%s<099> } {$mins} {$current_spot_color} {$spotname}; - } + }; }; #return {$spotstring}; -} +}; #ALIAS {dt} { #format {current_time} {%T}; @@ -311,13 +456,13 @@ #var {spot_groups_respawn} {}; #echo {.:: Death Timers ::.}; - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #if {"$killtimers[$spot][group]" == "true"} { #if {&spot_groups[$killtimers[$spot][name]] == 0} { #var {spot_groups[$killtimers[$spot][name]]} {$killtimers[$spot][time]}; #var {spot_groups_respawn[$killtimers[$spot][name]]} {$killtimers[$spot][respawn]}; }; - #if {$killtimers[$spot][time] < $spot_groups[$killtimers[$spot][name]]} { + #if {$killtimers[$spot][time] != 0 && $killtimers[$spot][time] > $spot_groups[$killtimers[$spot][name]]} { #var {spot_groups[$killtimers[$spot][name]]} {$killtimers[$spot][time]}; #var {spot_groups_respawn[$killtimers[$spot][name]]} {$killtimers[$spot][respawn]}; }; @@ -327,12 +472,12 @@ #var {spotstring} {@write_spot{$killtimers[$spot][name];$killtimers[$spot][time];$killtimers[$spot][respawn]}}; #echo {$spotstring}; - } + }; - #foreach {$spot_groups[]} {spot} { + #foreach {*spot_groups[]} {spot} { #var {spotstring} {@write_spot{$spot;$spot_groups[$spot];$spot_groups_respawn[$spot]}}; #echo {$spotstring}; - } + }; #echo {\n}; @@ -341,13 +486,13 @@ #echo {.:: Visit Timers ::.}; #format {current_time} {%T}; - #foreach {$visittimers[]} {spot} { + #foreach {*visittimers[]} {spot} { #if {"$visittimers[$spot][group]" == "true"} { #if {&spot_groups[$visittimers[$spot][name]] == 0} { #var {spot_groups[$visittimers[$spot][name]]} {$visittimers[$spot][time]}; #var {spot_groups_respawn[$visittimers[$spot][name]]} {$visittimers[$spot][respawn]}; }; - #if {$visittimers[$spot][time] < $spot_groups[$visittimers[$spot][name]]} { + #if {$visittimers[$spot][time] != 0 && $visittimers[$spot][time] > $spot_groups[$visittimers[$spot][name]]} { #var {spot_groups[$visittimers[$spot][name]]} {$visittimers[$spot][time]}; #var {spot_groups_respawn[$visittimers[$spot][name]]} {$visittimers[$spot][respawn]}; }; @@ -357,17 +502,17 @@ #var {spotstring} {@write_spot{$visittimers[$spot][name];$visittimers[$spot][time];$visittimers[$spot][respawn]}}; #echo {$spotstring}; - } + }; - #foreach {$spot_groups[]} {spot} { + #foreach {*spot_groups[]} {spot} { #var {spotstring} {@write_spot{$spot;$spot_groups[$spot];$spot_groups_respawn[$spot]}}; #echo {$spotstring}; - } + }; #echo {\n}; -} +}; -#ALIAS {writetimerstofile} { +#ALIAS {/writetimerstofile} { #system {> $xptimer_file}; #format {current_time} {%T}; #var {spot_groups} {}; @@ -375,13 +520,13 @@ #line {log} {$xptimer_file} {.:: Death Timers ::. }; - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #if {"$killtimers[$spot][group]" == "true"} { #if {&spot_groups[$killtimers[$spot][name]] == 0} { #var {spot_groups[$killtimers[$spot][name]]} {$killtimers[$spot][time]}; #var {spot_groups_respawn[$killtimers[$spot][name]]} {$killtimers[$spot][respawn]}; }; - #if {$killtimers[$spot][time] != 0 && $killtimers[$spot][time] < $spot_groups[$killtimers[$spot][name]]} { + #if {$killtimers[$spot][time] != 0 && $killtimers[$spot][time] > $spot_groups[$killtimers[$spot][name]]} { #var {spot_groups[$killtimers[$spot][name]]} {$killtimers[$spot][time]}; #var {spot_groups_respawn[$killtimers[$spot][name]]} {$killtimers[$spot][respawn]}; }; @@ -391,24 +536,24 @@ #var {spotstring} {@write_spot{$killtimers[$spot][name];$killtimers[$spot][time];$killtimers[$spot][respawn]}}; #line {log} {$xptimer_file} {$spotstring}; - } + }; - #foreach {$spot_groups[]} {spot} { + #foreach {*spot_groups[]} {spot} { #var {spotstring} {@write_spot{$spot;$spot_groups[$spot];$spot_groups_respawn[$spot]}}; #line {log} {$xptimer_file} {$spotstring}; - } + }; #line {log} {$xptimer_file} {\n\r\n\r.:: Visit Timers ::. }; #var {spot_groups} {}; #var {spot_groups_respawn} {}; - #foreach {$visittimers[]} {spot} { + #foreach {*visittimers[]} {spot} { #if {"$visittimers[$spot][group]" == "true"} { #if {&spot_groups[$visittimers[$spot][name]] == 0} { #var {spot_groups[$visittimers[$spot][name]]} {$visittimers[$spot][time]}; #var {spot_groups_respawn[$visittimers[$spot][name]]} {$visittimers[$spot][respawn]}; }; - #if {$visittimers[$spot][time] != 0 && $visittimers[$spot][time] < $spot_groups[$visittimers[$spot][name]]} { + #if {$visittimers[$spot][time] != 0 && $visittimers[$spot][time] > $spot_groups[$visittimers[$spot][name]]} { #var {spot_groups[$visittimers[$spot][name]]} {$visittimers[$spot][time]}; #var {spot_groups_respawn[$visittimers[$spot][name]]} {$visittimers[$spot][respawn]}; }; @@ -416,24 +561,58 @@ #continue; }; - #var {spotstring} {@write_spot{$visittimers[$spot][name];$visittimers[$spot][time];$visittimers[spot][respawn]}}; + #var {spotstring} {@write_spot{$visittimers[$spot][name];$visittimers[$spot][time];$visittimers[$spot][respawn]}}; #line {log} {$xptimer_file} {$spotstring}; - } + }; - #foreach {$spot_groups[]} {spot} { + #foreach {*spot_groups[]} {spot} { #var {spotstring} {@write_spot{$spot;$spot_groups[$spot];$spot_groups_respawn[$spot]}}; #line {log} {$xptimer_file} {$spotstring}; - } -} + }; +}; -#EVENT {SEND OUTPUT} {writetimerstofile}; +#EVENT {SEND OUTPUT} {/writetimerstofile;}; #FUNCTION {parse_spot} { #var {spot_sync_raw} {%0}; #replace {spot_sync_raw} {=} {;}; #list {spot_sync} {create} {${spot_sync_raw}}; #return {$spot_sync}; -} +}; + +#ALIAS {^dtload$} { + #class {dtsync} {clear}; + #class {dtsync} {read} {$xptimersync_file}; + #variable {killtimers} {$killtimers_sync}; + #variable {visittimers} {$visittimers_sync}; + + #showme {1}; + #format {dtsync_message} {[hotspots] <138>* Synced all available hotspot timers for %clast session%c from the disk.<099>} {Green}{}; + #echo {$dtsync_message}; +}; + +#ALIAS {^dtsave$} { + /writetimerstosyncfile; + + #showme {2}; + #format {dtsync_message} {[hotspots] <138>* Saved all available hotspot timers from %cthis session%c to the disk.<099>} {Green}{}; + #echo {$dtsync_message}; +}; + +#ALIAS {/writetimerstosyncfile} { + #nop Don't overwrite saved timers if we haven't recorded anything new; + #if {$xptimer_recent_flag == 0} { + #return; + }; + + #class {dtsync} {clear}; + #class {dtsync} {open}; + #var {killtimers_sync} {$killtimers}; + #var {visittimers_sync} {$visittimers}; + #class {dtsync} {close}; + + #class {dtsync} {write} {$xptimersync_file}; +}; #ACTION {%1 tells you: ruhs_dtsync=[%2] | ruhs_vtsync=[%3]} { #var {synccount} {0}; @@ -446,82 +625,83 @@ #list dtsync_list create {${dtsync}}; #list vtsync_list create {${vtsync}}; - #foreach {$dtsync_list[]} {spot} { + #foreach {*dtsync_list[]} {spot} { #var {syncitem} {@parse_spot{$dtsync_list[$spot]}}; #var {syncname} {$syncitem[1]}; #var {synctime} {$syncitem[2]}; - #foreach {$killtimers[]} {spotident} { + #foreach {*killtimers[]} {spotident} { #var {spotname} {$killtimers[$spotident][name]}; #if {"$spotname" == "$syncname"} { #var {killtimers[$spotident][time]} {$synctime}; #math {synccount} {$synccount + 1}; }; - } - } + }; + }; - #foreach {$vtsync_list[]} {spot} { + #foreach {*vtsync_list[]} {spot} { #var {syncitem} {@parse_spot{$vtsync_list[$spot]}}; #var {syncname} {$syncitem[1]}; #var {synctime} {$syncitem[2]}; - #foreach {$visittimers[]} {spotident} { + #foreach {*visittimers[]} {spotident} { #var {spotname} {$killtimers[$spotident][name]}; #if {"$spotname" == "$syncname"} { #var {visittimers[$spotident][time]} {$synctime}; #math {synccount} {$synccount + 1}; }; - } - } + }; + }; - #format {dtsync_message} {%c[XPTimer] %c*%c Synced %c%d%c spots from %c%s%c.} {bold blue}{yellow}{bold blue}{orange}{$synccount}{bold blue}{green}{%1}{bold blue}; + #format {dtsync_message} {[hotspots] <138>* Synced %c%d%c spots from %c%s%c.<099>} {Orange}{$synccount}{}{Green}{%1}{}; #echo {$dtsync_message}; -} +}; #ALIAS {dtsync %1} { #var {dtsync_msg} {}; #var {vtsync_msg} {}; - #foreach {$killtimers[]} {spotident} { + #foreach {*killtimers[]} {spotident} { #if {$killtimers[$spotident][time] > 0} { #format {dtsync_msg} {%s,%s=%d} {$dtsync_msg}{$killtimers[$spotident][name]}{$killtimers[$spotident][time]}; }; - } + }; #regex {$dtsync_msg} {^,{(.+)}$} { #var {dtsync_msg} {&1}; }; - #foreach {$visittimers[]} {spotident} { + #foreach {*visittimers[]} {spotident} { #if {$visittimers[$spotident][time] > 0} { #format {vtsync_msg} {%s,%s=%d} {$vtsync_msg}{$visittimers[$spotident][name]}{$visittimers[$spotident][time]}; }; - } + }; #regex {$vtsync_msg} {^,{(.+)}$} { #var {vtsync_msg} {&1}; }; #format {sync_message} {tell %s ruhs_dtsync=[%s] | ruhs_vtsync=[%s]} {%1} {$dtsync_msg} {$vtsync_msg}; #send {$sync_message}; -} +}; -#nop #EVENT {IAC SB GMCP room.info IAC SE} +#nop #EVENT {IAC SB GMCP room.info IAC SE}; #ALIAS {/spottimercheckroomid %1} { #var {room_info} {%1}; - #foreach {$visittimers[]} {spot} { + #foreach {*visittimers[]} {spot} { #regex {$room_info} {$spot} { #format {visittimers[$spot][time]} {%T}; - } - } -} + }; + }; +}; #ALIAS {gsdt} { #var {gsdt_string} {}; #var {spot_groups} {}; #var {group_spot_label} {}; - #foreach {$killtimers[]} {spot} { + #foreach {*killtimers[]} {spot} { #format {spotname} {$killtimers[$spot][name]}; #var {mins} {???}; #if {$killtimers[$spot][time] == 0} { #format {spotstring} {%s: ??? |} {$spotname}; + #continue; }; #else { #math {mins} {$current_time - $killtimers[$spot][time]}; @@ -535,7 +715,7 @@ #else { #format {spotstring} {%s: %dm |} {$spotname} {$mins} ; }; - } + }; #if {"$killtimers[$spot][group]" == "true"} { #if {&spot_groups[$killtimers[$spot][name]] == 0} { @@ -551,21 +731,22 @@ }; #format {gsdt_string} {%s %s} {$gsdt_string} {$spotstring}; - } + }; - #foreach {$spot_groups} {spot} { + #foreach {*spot_groups} {spot} { #format {gsdt_string} {%s %s} {$gsdt_string} {$group_spot_label[$spot]}; }; #var {spot_groups} {}; #var {group_spot_label} {}; - #foreach {$visittimers[]} {spot} { + #foreach {*visittimers[]} {spot} { #format {spotname} {$visittimers[$spot][name]}; #var {mins} {???}; #if {$visittimers[$spot][time] == 0} { #format {spotstring} {%s: ??? |} {$spotname}; + #continue; }; #else { #math {mins} {$current_time - $visittimers[$spot][time]}; @@ -579,7 +760,7 @@ #else { #format {spotstring} {%s: %dm |} {$spotname} {$mins} ; }; - } + }; #if {"$visittimers[$spot][group]" == "true"} { #if {&spot_groups[$visittimers[$spot][name]] == 0} { @@ -595,15 +776,15 @@ }; #format {gsdt_string} {%s %s} {$gsdt_string} {$spotstring}; - } + }; - #foreach {$spot_groups} {spot} { + #foreach {*spot_groups} {spot} { #format {gsdt_string} {%s %s} {$gsdt_string} {$group_spot_label[$spot]}; }; #regex {$gsdt_string} {^{(.+)} \|$} {#var {gsdt_string} {&1}; }; #send {group say $gsdt_string}; -} +}; #CLASS {xptimers} {close}; diff --git a/src/tpamonitor.tin b/src/tpamonitor.tin index 9dabe8b..8a36c80 100644 --- a/src/tpamonitor.tin +++ b/src/tpamonitor.tin @@ -1,16 +1,23 @@ #CLASS {tpamonitor} {kill}; #CLASS {tpamonitor} {open}; + #nop ### Turn TPA indicator on ### #action {^ * You are surrounded by a magical impact shield.$} { #var {tpa_status} {1}; #var {tpa_color} {invisible}; + #nop Update ourselves in the group shield monitor; + #variable {group_shields[$GMCP[charinfo][capname]][tpa]} {1}; + /softwritegroupshieldstofile; update_prompt; } #action {^ * You are surrounded by a %+ magical impact shield.$} { #var {tpa_status} {1}; #var {tpa_color} {%1}; + #nop Update ourselves in the group shield monitor; + #variable {group_shields[$GMCP[charinfo][capname]][tpa]} {1}; + /softwritegroupshieldstofile; update_prompt; } #action {^With a noise that sounds like "Plink!", everything around you flashes red for a moment.$} { @@ -20,18 +27,30 @@ #nop Update ourselves in the group shield monitor; #variable {group_shields[$GMCP[charinfo][capname]][tpa]} {1}; - /writegroupshieldstofile - update_prompt + /writegroupshieldstofile; + update_prompt; } #nop ### Turn TPA indicator off ### #action {^There is a sudden white flash. Your magical shield has broken.$} { - #line {log} {$chat_file}; #var {tpa_status} {0}; + #var {tpa_color} {0}; #format {tpa_end} {%T}; - /break_tpa + /break_tpa; + + #return @log_line_to_chat{}; } +#action {^You do not have any arcane or divine protection.$} { + #var {tpa_status} {0}; + #var {tpa_color} {0}; + + #nop Update ourselves in the group shield monitor; + #variable {group_shields[$GMCP[charinfo][capname]][tpa]} {0}; + #variable {group_shields[$GMCP[charinfo][capname]][ccc]} {0}; + #variable {group_shields[$GMCP[charinfo][capname]][ms]} {0}; + #variable {group_shields[$GMCP[charinfo][capname]][kii]} {0}; +}; #nop ### Track TPA colors ### #action {^As your shield absorbs the impact, it becomes visible as a %1 glow.$} @@ -64,24 +83,25 @@ #math {tpa_dur} {$tpa_end - $tpa_start}; #math {tpa_dur} {($tpa_dur - (60 - 1)) / 60}; #echo {<168>*** TPA broken! (lasted for $tpa_dur minutes) ***<099>}; - } + }; #nop Update ourselves in the group shield monitor; #variable {group_shields[$GMCP[charinfo][capname]][tpa]} {0}; - /writegroupshieldstofile + /writegroupshieldstofile; #unvar {tpa_start}; #unvar {tpa_end}; - update_prompt + update_prompt; } -#nop ### alias to reset TPA status, if it breaks and we didn't catch it ### +#nop ### alias to reset TPA status, if it breaks and we didn't catch it ###; #alias {tpareset} { #unvar {tpa_start}; #unvar {tpa_end}; #var {tpa_status} {0}; -} +}; #var {tpa_status} {0}; +#variable {tpa_color} {0}; #CLASS {tpamonitor} {close}; diff --git a/src/xpmonitor.tin b/src/xpmonitor.tin index c016b68..e590094 100644 --- a/src/xpmonitor.tin +++ b/src/xpmonitor.tin @@ -1,6 +1,8 @@ #CLASS {xpmonitor} {kill}; #CLASS {xpmonitor} {open}; +#nop window_xp = since last reset, session_xp = overall + #ALIAS {xp_monitor} { #var {previous_xp} {$latest_xp}; #var {latest_xp} {$GMCP[vitals][xp]}; @@ -8,40 +10,69 @@ #if {$previous_xp != 0} { #math {gained_xp} {$latest_xp - $previous_xp}; #if {$gained_xp > 0} { + #math {window_xp} {$window_xp + $gained_xp}; #math {session_xp} {$session_xp + $gained_xp}; }; }; #format {check_time} {%T}; - #format {uptime[hours]} {%m} {($check_time - $start_time) / 60 / 60}; - #format {uptime[minutes]} {%m} {($check_time - $start_time) / 60 % 60}; - #format {uptime[display]} {%th %tm} {$uptime[hours]} {$uptime[minutes]}; + #nop Calculate the window XP (how much earned since last xpreset); + #format {uptime[window][hours]} {%m} {($check_time - $start_time) / 60 / 60}; + #format {uptime[window][minutes]} {%m} {($check_time - $start_time) / 60 % 60}; + #format {uptime[window][display]} {%th %tm} {$uptime[window][hours]} {$uptime[window][minutes]}; + #format {xprate} {%d} { ($window_xp / ($uptime[window][hours] + ($uptime[window][minutes] / 60.0))) * 0.001 }; - #format {xprate} {%d} { ($session_xp / ($uptime[hours] + ($uptime[minutes] / 60.0))) * 0.001 }; -} + #nop Calculate the session XP (how much earned since client started up); + #format {uptime[session][hours]} {%m} {($check_time - $session_start_time) / 60 / 60}; + #format {uptime[session][minutes]} {%m} {($check_time - $session_start_time) / 60 % 60}; + #format {uptime[session][display]} {%th %tm} {$uptime[session][hours]} {$uptime[session][minutes]}; + #format {session_xprate} {%d} { ($session_xp / ($uptime[session][hours] + ($uptime[session][minutes] / 60.0))) * 0.001 }; +}; -#ALIAS {gsxp} { - #format {prompt_xp} {%Gxp in %s (%Gk/h)} {$session_xp} {$uptime[display]} {$xprate}; +#ALIAS {^gsxp$} { + #format {prompt_xp} {%Gxp in %s (%Gk/h)} {$window_xp} {$uptime[window][display]} {$xprate}; #send {group say $prompt_xp}; -} +}; + +#ALIAS {^gsxp all$} { + #format {prompt_xp} {Window: %Gxp in %s (%Gk/h) <|> Session: %Gmil in %s (%Gk/h)} {$window_xp} {$uptime[window][display]} {$xprate} {$session_xp / 1000000.00} {$uptime[session][display]} {$session_xprate}; + #send {group say $prompt_xp}; +}; + +#ALIAS {xpreport} { + #format {xpr_window} {%Gxp total in %s @ %Gk/h} {$window_xp} {$uptime[window][display]} {$xprate}; + #format {xpr_session} {%Gxp total in %s @ %Gk/h} {$session_xp} {$uptime[session][display]} {$session_xprate}; + #showme {=><= XP Report =><=}; + #showme {Window: $xpr_window}; + #showme {Session: $xpr_session}; +}; #ALIAS {xpreset} { - #var {session_xp} {0}; - #var {previous_xp} {0} + #var {window_xp} {0}; + #var {previous_xp} {0}; + #var {latest_xp} {0}; + #var {gained_xp} {0}; - #var {uptime[hours]} {0}; - #var {uptime[minutes]} {0}; - #var {uptime[seconds]} {0}; + #var {uptime[window][hours]} {0}; + #var {uptime[window][minutes]} {0}; + #var {uptime[window][seconds]} {0}; + #var {uptime[window][display]} {0}; #format {start_time} {%T}; #format {check_time} {%T}; - #format {uptime[display]} {%th %tm} {11} {0}; -} + #format {uptime[window][display]} {%th %tm} {11} {0}; +}; +#var {window_xp} {0}; #var {previous_xp} {0}; #var {latest_xp} {0}; #var {gained_xp} {0}; #var {session_xp} {0}; +#var {uptime} {}; + +#format {start_time} {%T}; +#format {check_time} {%T}; +#format {session_start_time} {%T}; #CLASS {xpmonitor} {close};