Skip to content

Documentation for moonwatch.lic

Mahtra edited this page Jul 28, 2026 · 1 revision

Moonwatch (v4.5.0)

moonwatch tracks the three DragonRealms moons (Katamba, Xibar, Yavash), the sun, and the full Elanthian calendar in real time. It tells you what is up right now, how long until the next rise or set, each moon's lunar phase, and the current date, season, and time of day. It works the instant you start it, with no setup and no network connection, on every game instance.

Everything it computes is published to UserVars, so other scripts (combat-trainer, gate, autocontingency, mm, the moonmage common library) read it directly.

See also: Technical Reference (how the model works and how the constants were derived) and Design History (the changelog and the retired Firebase design).


Table of Contents


Quick start

;moonwatch

That is all that is required. The script begins predicting immediately and keeps itself accurate by watching for moon and sun events in the game. Add it to your autostart so it is always running:

;e autostart('moonwatch')

To see status at a glance, add the moon alias once:

;moonwatch alias

Then type moon any time to print all three moons.


What it does

  • Predicts moon rise and set times to the exact game second.
  • Reports each moon's lunar phase (new, waxing crescent, first quarter, waxing gibbous, full, waning gibbous, third quarter, waning crescent) and an approximate countdown to the next phase.
  • Predicts sunrise and sunset across the seasons (the day is longest in summer, shortest in winter).
  • Tracks the full calendar: year, month, day, anlas, rois, season, and a named time of day such as "mid-morning" or "dusk".
  • Self-corrects. Every time you witness a moon or the sun rise or set, the script compares it to its prediction and tightens the result. You never have to do anything.
  • Works offline and on every game instance (Prime, Platinum, Fallen, Test). There is no shared database and no network traffic.

Commands

All toggle settings are saved per character and persist across sessions until you turn them off.

Command Effect
;moonwatch Start tracking.
;moonwatch debug Turn on debug output (stays on for future runs).
;moonwatch nodebug Turn off debug output.
;moonwatch window Open a small moon status window (stays on for future runs).
;moonwatch nowindow Close the moon status window.
;moonwatch alias Add a global moon alias that prints all three moons.
;moonwatch log Start logging events to CSV for calibration, and auto-observe each moon at rise (this character).
;moonwatch nolog Stop logging events and rise auto-observe.
;moonwatch reset Reset all offsets to zero and exit. Use if predictions look wrong.

You can combine the start command with toggles, for example ;moonwatch debug window.


The status window

;moonwatch window opens a compact window showing all three moons in short form:

[k]+(45) [y]-(12) [x]+(78)

The letter is the moon (k, y, x). A plus means it is up; a minus means it is down. The number is minutes until the next event. So [k]+(45) means Katamba is up and sets in 45 minutes, while [y]-(12) means Yavash is down and rises in 12 minutes.

The window updates whenever the minute value changes and refreshes itself once a minute even when the game is quiet, so it stays correct after a reconnect or an idle stretch.


The moon alias

After running ;moonwatch alias, typing moon prints the full status of all three moons, for example:

katamba is up for 45 minutes (waxing crescent) : yavash will rise in 12 minutes (new) : xibar is up for 78 minutes (full)

The alias is self-aware: it checks whether moonwatch is actually running before it reads the data. If moonwatch is not running, typing moon tells you so instead of echoing stale numbers:

moonwatch is not running - start it with ;moonwatch

The alias also keeps itself up to date automatically. When a new version of moonwatch changes what the moon alias should print, the next ;moonwatch start silently upgrades your existing alias in place -- you do not have to re-run ;moonwatch alias. moonwatch only ever touches a moon alias it recognizes as its own; if you have created your own moon alias for something else, it is left alone, and moonwatch never creates the alias unless you asked for it with ;moonwatch alias.


Using moonwatch data in other scripts

moonwatch publishes everything to UserVars, which any other script can read. These fields are kept up to date every loop while moonwatch runs.

Moons

UserVars.moons['visible']                   # array of moons currently up, e.g. ['katamba','xibar']
UserVars.moons['running']                   # true while moonwatch is maintaining this data
UserVars.moons['katamba']['visible']        # true if Katamba is up
UserVars.moons['katamba']['timer']          # whole minutes until next event
UserVars.moons['katamba']['timer_seconds']  # seconds until next event
UserVars.moons['katamba']['t']              # 0..1 progress across the current arc
UserVars.moons['katamba']['phase']          # 'waxing crescent'
UserVars.moons['katamba']['phase_index']    # 0..7 (0 = new)
UserVars.moons['katamba']['next_phase']     # upcoming phase name
UserVars.moons['katamba']['next_phase_seconds'] # approx seconds to next phase
UserVars.moons['katamba']['pretty']         # "katamba is up for 45 minutes (waxing crescent)"
UserVars.moons['katamba']['short']          # "[k]+(45)"

Sun

UserVars.sun['day']                     # true if the sun is up
UserVars.sun['night']                   # true if the sun is down
UserVars.sun['visible']                 # same as 'day'
UserVars.sun['timer']                   # whole minutes until next event
UserVars.sun['timer_seconds']           # seconds until next event
UserVars.sun['season']                  # 'winter' | 'spring' | 'summer' | 'fall'
UserVars.sun['time_of_day']             # e.g. 'mid-afternoon'
UserVars.sun['pretty']                  # "sun sets in 45 minutes (mid-afternoon)"
UserVars.sun['short']                   # "[S]+(45)"
UserVars.sun['running']                 # true while moonwatch is maintaining this data

Calendar

UserVars.calendar['date_string']        # "446-01-15 06:12"
UserVars.calendar['year']               # 446
UserVars.calendar['month']              # 1..10
UserVars.calendar['day']                # 1..40
UserVars.calendar['day_of_year']        # 0..399
UserVars.calendar['anlas']              # 0..11 (DR hour)
UserVars.calendar['rois']               # 0..29 (DR minute)
UserVars.calendar['season']             # season string
UserVars.calendar['time_of_day']        # named period
UserVars.calendar['year_name']          # "Year of the Emerald Dolphin"
UserVars.calendar['month_name']         # "Akroeg the Ram"
UserVars.calendar['anlas_name']         # "Phelim's Vigil"
UserVars.calendar['running']            # true while moonwatch is maintaining this data

Scripts that read UserVars.moons['visible'], the per-moon timer, and UserVars.sun['day']/['night'] (such as the combat trainer, gate, autocontingency, mm, and the moonmage common library) work unchanged.

The ['running'] flag is optional and non-destructive: it is true while moonwatch is live and set to false when moonwatch exits cleanly, without removing any other keys. A consumer that wants to know whether the data is fresh can check it; existing consumers that ignore it are unaffected.


Accuracy and self-correction

Predictions are accurate the moment the script starts, with no warm-up.

  • Moons are predicted to the exact game second. The game fires moon events on 60-second boundaries, and moonwatch computes the precise boundary rather than an average, so it does not drift. When you witness a rise or set, moonwatch only adjusts if its prediction was genuinely on the wrong tick, so a correct prediction is left exactly where it is.
  • The sun is predicted from an empirical day-of-year table built from observed sun events, so its cold-start prediction is essentially exact. (A seasonal cosine is kept only as a fallback for any uncovered day.) It also self-corrects from the first sunrise or sunset you witness.
  • Lunar phase is computed from the DR client's own orbital constants. It is model-only (there is no passive phase broadcast to correct against) but has been validated against the in-game observe <moon> verb.

If predictions ever look clearly wrong, run ;moonwatch reset and start the script again. It will recalibrate from your next observed events.


Game instances

moonwatch works identically on Prime, Platinum, Fallen, and Test. The moon and sun periods are game-code constants that are the same on every instance, and each character's correction offsets are stored per instance and per character, so instances never bleed into each other. On a non-Prime instance the script prints a one-time startup notice that the absolute phase self-calibrates from your observed rise/set events; predictions converge after the first one.


Calibration logging

If you want to contribute calibration data or re-derive the constants, enable logging with ;moonwatch log. It writes CSV files to your Lich data directory (moon events, sun events, and moon-phase observations) recording the raw observed intervals and phase wordings. While logging is on, moonwatch also auto-observes each moon as it rises to capture its phase readout. Disable with ;moonwatch nolog. Logging is per character and off by default. See the Technical Reference for the file formats and the re-derivation procedure.


Troubleshooting

Predictions are wrong. Run ;moonwatch reset, then ;moonwatch again. The model recalibrates as you observe events. Watching one full cycle of each body is enough to lock it in.

The window shows zeros or stale data. Kill and restart: ;kill moonwatch, then ;moonwatch window. The window cache is cleared on every start.

Typing moon says moonwatch is not running. That is the alias working correctly -- start moonwatch with ;moonwatch.

Typing moon shows the same numbers every time. You have an older moon alias from before the self-aware version and moonwatch has not upgraded it yet (for example it has not been restarted since you updated). Run ;moonwatch alias once to install the current alias; after that it self-updates on start.

Nothing seems to update. Confirm the script is running with ;list. Other scripts that depend on moon data will start moonwatch automatically if it is not already running.


Changelog

v4.5.0

  • Instance-awareness. The moon and sun periods are game-code constants (cross-validated against the DR client's own hard-coded sidereal periods to within ~0.05s), so they are identical on every instance; offsets are already scoped per instance and per character, so each instance self-calibrates independently. Non-Prime instances print a one-time startup notice.
  • Self-aware, self-updating moon alias. The alias now reports when moonwatch is not running instead of echoing stale data, and an existing moonwatch alias auto-upgrades to the current body on the next start (read from the alias service database). It never creates an alias you did not ask for and never overwrites an unrelated moon alias.
  • UserVars.*['running'] freshness flag. Non-destructively marks moon/sun/ calendar data live at startup and stale on exit.

v4.4

  • Lunar phase. Each moon now reports its phase and an approximate next-phase countdown, exposed via UserVars (phase, phase_index, next_phase, next_phase_seconds). While logging is on, moonwatch auto-observes each moon at rise to capture the game's phase wording and validate the model.

v4.2

  • Drift-free moon periods. The moon cycle constants use the game's true fractional periods instead of rounded integers, so moon predictions no longer drift over weeks and offsets stay near zero on their own. After updating, run ;moonwatch reset once per character to clear old offsets (the internal moon anchors moved).

v4.1

  • Empirical sun lookup table. Sunrise and sunset are read from a day-of-year table built from observed sun events, replacing the seasonal cosine (kept only as a fallback). This makes the sun prediction essentially exact from a cold start. UserVars.sun fields are unchanged.

v4.0

  • Bresenham tick prediction for moons, replacing an average that was always a little off.
  • Local self-correction only; corrections come entirely from your own observed events.
  • Removed Firebase and the correct argument. No network calls, no shared database. If you have correct in an autostart or profile, remove it.
  • Full calendar exposed via UserVars.calendar, plus timer_seconds alongside the whole-minute timer.

For the complete version history (back to v2.0) and the retired Firebase design, see Design History.

Clone this wiki locally