promotional bannermobile promotional banner

EasyWarCrates

Predicts where a War Supply Crate transport is headed while it is still in the air, and tells your raid.
Back to Files

v0.1.0

File nameEasyWarCrates-v0.1.0.zip
Uploader
dazmagardazmagar
Uploaded
Sep 26, 2026
Downloads
9
Size
167.3 KB
Flavors
Retail
File ID
8977431
Type
R
Release
Supported game versions
  • 12.1.0

What's new

EasyWarCrates

v0.1.0 (2026-09-26)

Full Changelog Previous Releases

  • Describe the release as it actually is
    The changelog was written before a day of work that changed what this addon
    does: it reads the countdown line a raid leader posts, keeps the cycle after
    somebody else's crate is looted, marks Spectral Battle Chests, stays quiet in
    chat by default, and reports a locale gap rather than failing silently in it.
    A first release gets read by people deciding whether to install it. Listing six
    things it did yesterday would have been the wrong six.
  • Give the chest its own row whenever the zone's row is already busy
    It was splitting only when a crate was in the air or coming down. A countdown
    was not counted as the row having something to say, so once Slayer's Rise had
    one -- which it now does the moment a raid posts theirs -- the chest went back
    to riding along, and the row read "SR 45 spectral" beside a pair of times with
    nothing saying the chest was on the ground at all.
    The numbers on that row are about the crate. The chest has no countdown and
    wants none: it is there now and gone in a minute or two. So it rides along only
    when the zone's row would otherwise be blank, which is what "nothing else to
    say" was always meant to mean.
    The spec for the quiet case was seeding a timer, so it had been testing the
    busy case under the wrong name. Its Scanner stub was also missing ActiveZones,
    which is what earns a zone a row when nothing is timed there -- so the quiet
    case had no row to check at all and passed by accident.
  • Keep the cycle a raid hands you after its crate is gone
    Dmitrii never flew to Zul'Aman. The raid watched the transport, the parachute
    and the landing there, this addon showed every stage of it, and then the row
    went blank as though nobody had seen anything -- while the leader was standing
    over the crate he had just looted.
    A received sighting of a crate on the ground is worth showing for ninety
    seconds, and that is right: a row still saying ON THE GROUND ten minutes on
    sends somebody across a zone for something already looted. But the sighting
    carries two facts and only one of them expires. The crate is gone; the moment
    it landed is not, and that pins the shard's cycle for hours.
    So a landed report settles into an anchor instead of evaporating, at the time
    it actually landed. Which exposed the other half: anchors drew nothing at all.
    Remote.For refuses them on purpose -- an anchor says a crate existed, not that
    one is in the air, and a live row claiming otherwise sends a raid somewhere
    nothing is happening. True, and it left anchors feeding only the shard lookup.
    An anchor cannot make a live row and can perfectly well make a countdown, which
    is now what it makes, in the same columns a timer of our own fills.
    A zone gets that row even with nothing timed there, which is the case being
    fixed. Otherwise a countdown only appears once this client has been to the zone
    itself -- and the whole point of a raid that splits up is that it does not have
    to be.
    The row carries the dot that marks somebody else's word, because their anchor
    is about the copy of the zone they were in. Our own timer still wins outright.
    This is also what makes the countdown line read last night worth anything. It
    was parsed into anchors, and anchors drew nothing, so it had been quietly
    achieving very little.
  • Survive a locale this addon has never seen
    An audit before publishing, because most players are not on an English realm.
    The logic holds: vignettes are matched by id and never by name, zones by map
    id, the faction comes back as an English token, and GetLocale is never read.
    Nothing decides anything from localised text. Two places degrade, though, and
    one of them degrades silently.
    Zone names were shipped English and shown as such. A German player reading
    "Eversong Woods" in a window while their game says Immersangwald is being told
    about somewhere else, and the game answers the question for free. Asked once
    per zone and cached, falling back to the shipped name when it will not.
    The silent one is the announcer. Its name is localised as much as its lines
    are, and only English and Russian are written down -- so on any other client
    the name test fails on every line and the yell anchor, one of the three things
    the empty window promises, never fires. Worse, the refusal happened before the
    log, so /ewc yells showed nothing and "no announcer on this locale" was
    indistinguishable from "nobody has spoken".
    Speakers a crate zone hears that are not known announcers are now counted on
    the way past, with the line they said, and /ewc yells reports them: if a cycle
    started when one of those spoke, that line is what this addon needs. Bounded to
    a tracked zone and a dozen names, which is a ceiling against a surprise rather
    than a budget.
    Same shape as the zone names heard on the wire, and that one has already paid
    for itself twice this evening.
  • Read more of what the raid is already saying
    Two gaps in the same place: RCT posts plenty in chat and this addon was reading
    one line of it.
    The countdown, every cycle, for every zone the raid is watching:
    Next Crate: Zul'Aman - 1258 in 02:46
    Next Crate: Slayer's Rise - 45 in - 20 s
    Zone, shard and time remaining -- a timer, in plain text, from whoever is
    leading. Only the "Flying in X" alert was being read, and that fires for the
    one zone happening now. Dmitrii sat in Slayer's Rise for two hours while a raid
    broadcast the above for five zones, and watched his rows age out one reload at
    a time.
    Turned into an anchor rather than carried as a countdown: a spawn one interval
    before the announced drop puts the same cycle in the same phase, and everything
    downstream already knows what to do with a spawn. Filed as somebody else's word
    like every received report -- their arithmetic, not a crate anyone saw -- so it
    cannot overrule a sighting or become a stored timer on its own. Refused past a
    whole cycle, because beyond one interval it is not counting to the next drop
    and an anchor from it would put the phase somewhere it has never been.
    And two zone names, which the same raid supplied. RCT sends its alert using the
    sender's own localised name and the game will only tell a client the names in
    its own locale, so a German raider's report was unreadable here. Leerensturm
    and Schlaechteranhoehe are now written down, each resolved from the wire rather
    than translated: the name arrived carrying a shard, and that shard was one this
    client had independently confirmed for a zone in its own timers and phase
    memory at the same moment. The morphology agrees separately, which is two lines
    of evidence rather than one.
    The weakness is recorded beside them. Shard ids are not unique across zones and
    Slayer's Rise is nested inside Voidstorm, so the record of untracked vignettes
    shows both ids under both names. What makes it tolerable is the shape of the
    mistake available: confusing those two sends somebody to a zone they are
    standing in the parent of, where the same error between Eversong and Harandar
    would send them across a continent. Anything unresolved is still collected
    rather than dropped, which is how these two arrived.
  • Leave room for the row the chest now takes
    Six zones is a full rotation and already six rows. Giving the chest one of its
    own makes seven, and a zone with a transport overhead but nothing timed takes
    one as well -- so a cap of eight was one surprise away from dropping a row
    without saying so, which reads as the addon not having seen something.
    Raised rather than made clever. The window grows with what it has to show and
    ten is past anything a rotation produces.
  • Give the chest its own row once the zone has a crate as well
    Two cases, and they want different answers. On a quiet zone the chest rides on
    that zone's row: one place, one line, and the row has nothing else to say. Once
    a transport is in the air or a crate is coming down there, one row is being
    asked to carry two things that are collected separately, at different times --
    and a click on it can only mean one of them.
    Split, each row says one thing and a click does what the row says. The crate
    row drops the chest when the split happens, so they cannot both claim it.
    The chest row is ranked with a crate already on the ground, because both are
    there to be collected now. A transport three minutes out is not, which is what
    put "transport in the air" into a raid warning while a chest lay in the zone.
    Its row draws and describes itself as a chest rather than as a crate: the live
    entry it carries exists to rank it and fill the bar, and reading that as a
    crate would have printed ON THE GROUND, OURS about somebody else's chest.
    Also fixes the record of untracked vignettes, which this uncovered. It
    deduplicated by GUID, and a vignette's GUID churns while the object stays put
    -- which is already why the transport is announced per zone rather than per
    track. Slayer's Rise reported eighty-seven sightings of one set of remains and
    "repeats about every 0s", which is not a measurement, it is the poll rate. Keyed
    by what the object is and where it is now, with five minutes before a sighting
    counts as a new one, and a period is only reported when the readings cluster
    and the figure could be a cycle at all.
    On Dmitrii's own data that now reads: Spectral Battle Chest, about every 1800s,
    give or take 24. Thirty minutes, which is what the wiki says, arrived at from
    his sightings rather than copied from it.
  • Restore ns.Print, which a blanket rename had taken with it
    Moving the event narration onto ns.Say was done with a string replacement over
    the whole of Core/Main.lua, and "ns.Print(" appears in that file in one place
    that is not a call: its own definition. So ns.Print stopped existing, all 147
    calls in Core/Commands.lua became nil calls, and every /ewc command went quiet.
    Quiet, not broken-looking. The dispatcher catches a handler that throws and
    reports it -- through ns.Print. So the report failed the same way the command
    did, and a player typing /ewc scan got nothing at all: no output, no error, no
    reason.
    The harness could not have caught it, because the harness was the thing hiding
    it. FRESH() replaced ns.Print with its own collector before anything ran, which
    is to say it supplied a working copy of the one function that had gone missing.
    It now leaves all three alone, captures the global print instead -- where a
    command's answer actually ends up -- and checks outright that Print, Say and
    Debug exist before calling anything.
    Renaming ns.Print away again was watched turning it red, naming both the
    missing function and the commands that fell over, and green again once put back.
  • Stop talking in chat unless asked to
    An addon that narrates unprompted is one people route into a spare tab or
    uninstall, and a first-time player reading forty lines about shards and descent
    readings has been handed a diagnostic channel, not a feature. Off by default
    now, with a checkbox and /ewc chatter.
    The split is between what the addon says on its own and what it says because
    somebody just did something. Every event-driven line in Core/Main.lua and
    Detect/Scanner.lua moves to ns.Say and can be silenced. The commands, the
    window and the panels keep ns.Print: those are answers, and an answer that does
    not arrive is a bug.
    Two exceptions, both deliberate. The load line stays loud -- one line a session,
    and the only place a new player is told there are commands at all. And verbose
    implies this, because turning on the detailed mode to find out what is
    happening and getting silence would be absurd.
    The log is never silenced. db.log is what a failure gets diagnosed from after
    the fact, and it is how Dmitrii's logs reach me at all; a switch that emptied
    it would trade a real capability for a cosmetic one. tools/firstrun.py drives
    all three switch positions and checks the chat lines and the log lines
    separately -- silencing the log alongside the chat was watched failing it
    before this was committed.
  • Shift-click the title to name the addon, and nothing more than name it
    Dmitrii asked for this and then asked for the download sites in it as well.
    Blizzard's addon policy is explicit on the second part: an addon may not be
    used to advertise goods or services, and pointers to where it is distributed
    belong on its own site rather than in the game. So the message is the name and
    what it does, with no store and no link.
    The line is worth drawing because the two cases are genuinely different. A
    player telling their raid what they are running is that player speaking, and
    the addon is composing the sentence for them. An addon putting store links into
    chat by itself is the thing the policy names. Keeping to the first costs
    nothing here -- anyone who wants it can search the name.
    Shift held, because the title is the natural place to grab a window by and
    moving it must not speak to twenty people. The button over the title forwards
    dragging for the same reason: taking that away to gain a click would be a poor
    trade.
    Never a raid warning, even from a lead who could send one. This does not
    warrant interrupting a raid, and the row announce is where that channel earns
    its place. A minute's cooldown, because a button that talks to a group and can
    be held down is a button that will be.
    tools/firstrun.py drives it from a raid lead and checks both the returned
    channel and the one the send actually used; inverting the rule so it took the
    warning was watched failing before this was committed.
  • Point the packager at the Wago project too
    Both stores are now named in the .toc, which is where the packager decides
    where a token uploads. Wago matters more than a second listing: CurseForge
    refused WowUp's application for API access and Wago partnered with WowUp
    instead, so this id is how a WowUp user reaches this addon at all.
  • A template for the tokens, and a check that does not cry wolf
    .env.example documents the two names and where each one really belongs, with no
    value in it. The workflow cannot read a .env at all -- it runs on GitHub's
    servers -- so the file is only for releasing from a machine, and the comment
    says so rather than leaving someone to find out by watching a release do
    nothing.
    The check flagged the template. Its own fault: \s crosses newlines, so an empty
    CF_API_KEY= ran on and matched WAGO_API_TOKEN on the line below as though that
    name were a value. Spaces and tabs only now. A rule that reports a file with no
    secret in it gets switched off, and then it is worth nothing.
    Both directions watched again afterwards: the template green, a .env forced
    into the index with a real-shaped token red, and green once more with it gone.
  • Refuse a token rather than rely on the ignore file
    Dmitrii wants a local copy of the upload tokens in a .env, which the BigWigs
    release script reads and which is the supported way to release from a machine
    rather than from CI. Reasonable, and it is also the one mistake in this
    repository that a later commit cannot undo: a token in a public repo has to be
    revoked and reissued, and every install between those two moments had it.
    .gitignore alone is not the guard. git add -f walks past it, and git add -A
    has swept unintended files into commits twice on this project in a day.
    So the suite refuses one. Shapes rather than entropy, because a CurseForge or
    Wago token is a UUID and reads as ordinary text -- what gives it away is the
    name it is assigned to. The workflow's own CF_API_KEY: ${{ secrets.CF_API_KEY }}
    is not a leak and is not reported.
    Watched failing on all three shapes before this was committed: a .env forced
    into the index with both token names, and a GitHub token pasted into a tracked
    file. Then removed, and watched going green again.
  • Point the packager at the CurseForge project, and write the first changelog
    X-Curse-Project-ID is what decides where a token uploads. Without it that site
    is skipped without complaint, which is the kind of silence worth removing now
    rather than reading a green workflow run and wondering why nothing appeared.
    The changelog is written rather than generated. With no earlier tag the
    packager has ninety commits to summarise and would hand a reader all of them;
    what someone deciding whether to install this wants is the six things it does
    and the one thing it refuses to do.
    X-Wago-ID follows when that project exists. Same rule: no id, no upload there.
  • A logo drawn from what the addon does
    CurseForge requires one to create a project at all, at 1:1 and no smaller than
    400 across, and refuses a solid or gradient colour, a game logo or a
    trademarked asset. So this is the crate under its parachute with the spot it is
    predicted to land on, in the three colours the addon already prints in: the
    teal of its own chat prefix, the amber of a countdown, the green of a call it
    is sure about.
    Generated rather than drawn by hand, so it is reproducible and the geometry is
    readable as numbers. tools/make_logo.py renders at four times and downsamples,
    then reopens the file and checks what actually landed -- square, at least 400,
    and enough distinct colours that it cannot be the flat fill the rules refuse.
    It stays out of the published addon. .pkgmeta ignores assets and the .toc never
    listed them, so it belongs to the listing page and not to the download.
  • Say how to work on this before anyone asks
    What a contributor needs that the code does not say: that lupa is the only
    dependency, that the suite runs the addon's real Lua rather than a mock of it,
    and that the line in the .toc where files stop being testable is the line where
    they start creating frames at load.
    Two traps get their own section because each cost a shipped bug. The test
    interpreter is Lua 5.5 and the game is a 5.1 dialect, so goto parses green here
    and dies in the client. And a local read above its own declaration is a nil
    global rather than an error, which fails in game and nowhere else -- the lint
    catches it now because it did not catch it the first time.
    The house rules are the ones this project actually runs on: show a check red
    before trusting it green, look at the artefact rather than the exit code,
    measure rather than assume, keep the vignette ids in one file, bundle nothing.
    Each is there because ignoring it cost something.
  • Rank the chest by when it can be taken, and say where it is
    Dmitrii clicked the Slayer's Rise row with a chest lying in it and the raid was
    told "transport in the air". Mine: I ranked a crate this client can see above
    the chest on the reasoning that both mean go now. A transport in the air does
    not mean go now, it means in three minutes -- his own log has one at 3:03 --
    while the chest is on the ground and gone in one or two. A crate already on the
    ground still outranks it, because that one really is there to collect and is
    why anyone is in the zone.
    The line now carries coordinates, which a crate line deliberately does not. A
    crate leans on the pin travelling with it, and the pin only yields a clickable
    link when the waypoint readback agrees about the map. A crate survives that
    failing: it has a countdown and a zone, and it will still be there. The chest
    has neither, so "SPECTRAL CHEST in Slayer's Rise" with no link has told the
    reader nothing they can reach in time.
    What is documented about it is now written beside its id, because there is very
    little: object 527903, added in 12.0.1, spawning every thirty minutes in
    Slayer's Rise for up to five players of either faction. Whether that half hour
    runs from the spawn or from the last loot is not recorded anywhere, nor where
    in the zone it appears, nor how long it stays. The wiki page has no screenshot.
    So the figure sits in a comment with its source and nothing is built on it, and
    the record of untracked vignettes measures the rest -- the way every interval
    in this addon was arrived at.
  • Credit the characters the numbers were measured on
    Dmitrii asked for his accounts in the credits, and framed as what they actually
    are they belong there: nearly every figure this addon relies on -- the descent
    times, the cycle lengths, the release bias, the drop points -- was read off
    those characters across four realms and both factions rather than assumed or
    copied from another addon. That is the part worth saying.
    The copyright line stays one holder. A licence names who to ask, not who
    played.
  • Say who wrote this and on what terms
    A public repository with no licence is all rights reserved by default, which
    for an addon is the wrong answer by accident: nobody could fork it, send a
    patch, or redistribute it, and CurseForge asks for one when a project is
    created. MIT, which is what the addon world runs on and what WarCrateTracker
    uses -- the addon the Credits already borrow a convention from.
    Nothing here is anybody else's to license. LibDeflate and AceSerializer are
    borrowed at runtime through LibStub from whichever addon already loaded them,
    so none of it ships and none of it carries terms into this.
    One author, not a roster. Dmitrii offered all eleven of his characters and the
    copyright line takes a holder rather than a list -- and a cross-realm roster in
    a public repository lands in every user's addon folder, where it cannot be
    taken back. Dazm is the main and matches the handle the commits already carry.
    X-Curse-Project-ID and X-Wago-ID stay out until those projects exist. They are
    what tells the packager where to upload, and a wrong id uploads this over
    somebody else's addon.
  • Publish to both stores from one tag
    CurseForge refused WowUp's application for API access and Wago partnered with
    WowUp instead, so the two are not alternatives: a player running WowUp can only
    reach this addon through Wago, and CurseForge's own client only through
    CurseForge. Publishing to one leaves half the audience unable to install it.
    The BigWigs packager does both, plus a GitHub release, from one annotated tag.
    Which project each token uploads to comes from the .toc -- X-Curse-Project-ID
    and X-Wago-ID -- so a missing id means that site is silently skipped, which is
    worth knowing before reading the first run's log and wondering.
    .pkgmeta gained assets, dist and .github. The screenshots belong on the listing
    page rather than inside the download, and dist is the archive this repo builds
    for itself. tools/package.py refuses the same names and then reads its own
    archive back, so a name missed in one is still caught by the other before
    anything is uploaded.
    No tokens here or anywhere in this repository. They go in the repository's
    Actions secrets, where the workflow reads them and nobody else does.
  • Ignore the build output
    tools/package.py writes the publishable archive to dist/. Built from the repo
    on demand and never committed: the thing that gets uploaded should be produced
    from a known commit, not carried alongside one and liable to drift from it.
    tools/ itself is already ignored, so the script lives beside deploy.py and
    loglens.py where the other things that are not the addon live.
  • Screenshots for publication
    Dmitrii's, taken in Midnight 12.2: the window in each of the states it has
    (inbound, landing, on the ground), the settings and data panels, and the
    Slayer's Rise row carrying a Spectral Battle Chest.
    Tracked rather than ignored, unlike tmp/ and tools/. These are part of what
    gets published, not notes that must not be.
  • Let a click announce the chest, and pin what it actually said
    Dmitrii saw one, the row marked it, and clicking said "nothing is known about
    that zone worth announcing". That refusal was mine: the tooltip only offers a
    click when Model.Announcement produces a line, and nothing here produced one for
    a row carrying only a chest.
    It is the thing most worth announcing of anything on that row. It lasts a
    minute or two, it is drawn from anywhere in the zone so anyone in there can
    reach it, and unlike the crate nobody else's addon is broadcasting it.
    Ranked where it belongs rather than bolted on the end. A crate this client can
    see outranks it, since both mean go now and the crate is why people are there.
    A crate that arrived through RCT does not: that was a raid warning everyone has
    already read, and the chest was not in it. And a chest on the ground beats a
    countdown to a crate that is not there yet.
    Announcement now names its subject and AnnounceRow pins that one. A row can
    carry a crate and a chest at once, and a line about one with a pin on the other
    sends the reader somewhere else with our own link as the evidence -- the same
    fault as the tooltip not naming its channel, and worth closing the same way.
  • Mark the chest on the zone's own row, and catch the bug that shipped doing it
    Dmitrii asked for the Slayer's Rise row to carry something like "spectral"
    beside the shard, and that is the right shape: the chest is a second object in
    the same copy of the same zone, not a stage of the crate. Two rows for one place
    is the RCT complaint. So it rides on the row, beside the shard, and the
    countdown column stays the crate's.
    Its id is kept out of the crate stage table deliberately. Nothing that reasons
    about crates should be able to mistake it for one -- there is no transport to
    fit a heading to, no parachute to time, and no release. What it needs is to be
    noticed, placed, and counted.
    A zone with a chest and no crate timer still earns a row, through the same path
    that gives one to a zone with a transport overhead and nothing timed. The grace
    before it is believed gone is short, fifteen seconds rather than the crate's
    ninety, because Dmitrii reports the vignette is drawn from anywhere in the zone:
    absence is an answer here, not the player having flown out of range.
    Two things fixed on the way. A row that exists only for the chest has nothing to
    post, and the tooltip was promising a left-click would post it anyway -- the
    same fault as not naming the channel, so the line only appears when there is
    something to say. And the record of untracked vignettes keeps filing this one
    even though it is now recognised, because that record is what measures its
    cycle and being able to draw it is no reason to stop learning.
    The lint gained the rule that should have caught what I shipped mid-change.
    liveSpectral was declared beside the tables it belongs with, six hundred lines
    below the function that iterates it, and pairs(liveSpectral) is a call to pairs
    with a nil global as its argument: tests green, lint green, deploy green, /ewc
    show dead. The old rule only caught a local being CALLED before its
    declaration. Reading one is the same bug, it now reports it, and moving the
    declaration back down was watched turning it red.
  • Trust a stranger's position only on its own zone map
    The crate path checks that a vignette's position resolved against the zone map
    before using it, because a vignette can answer against a different map and the
    coordinates then point somewhere the player is not. The new stranger record
    stored whatever came back. Guarded the same way, and a reading refused is
    counted rather than dropped silently -- if 6892 turns out never to give a
    position this client can use, that is the thing worth knowing about it.
  • Keep a record of the vignettes we do not track
    Dmitrii asked how to track Spectral Battle Chest, vignette 6892, which also
    drops in Slayer's Rise. Nothing anywhere can answer that yet: none of RCT,
    WarCrateTracker or CrateTrackerZK tracks it, and this addon had no memory of any
    id it does not already know.
    Three facts decide whether it can be tracked and all three are observations,
    not deductions. Which zones it appears in. Whether it repeats. On what period.
    His own account is that it simply appears on the ground, which if it holds means
    there is no transport to fit a heading to and no parachute to time -- a spawn
    catalogue and a cycle would be the whole of it, and none of the flight machinery
    applies.
    So every vignette with an unknown id is now filed as it appears: name, atlas,
    which zones, where, and the shard each sighting came from. Deduplicated by GUID,
    because a vignette stays drawn for minutes and every poll would otherwise read
    as another spawn. /ewc scan prints the history under the live sweep and computes
    a period once three gaps on one shard agree, using the same densest-run
    estimator the intervals use.
    The shard travels with each sighting for the reason it matters everywhere else
    here: two copies of a zone run their own cycles, so a gap paired across them
    measures nothing.
    Worth having beyond this chest. Every id in Data/Vignettes.lua came from three
    addons built against interface 120100, and if Midnight renumbers them the crate
    stops being recognised -- at which point this record is what says what the new
    numbers are doing.
  • Keep the shard when the zone name is in a language we cannot read
    RCT broadcasts its chat alert using the sender's own localised zone name, and
    the game will only ever tell a client the names in its own locale. So a German
    raider's "Leerensturm" resolves to nothing here and the whole report was
    dropped, shard and all.
    One public raid produced three of them: Leerensturm and Schlaechteranhoehe from
    a German client, and a Russian name besides. Those senders' flying calls are
    exactly the reports worth having, and they were the only ones being thrown away.
    The shard survives the name, and it is the handle on which zone was meant, so
    FromAlert hands it back with the name and Comm files the name against the
    shards it arrived with. A shard that also turns up in a report whose zone IS
    readable settles the mapping.
    Deliberately not resolved from the shard automatically. Shard 39 has been seen
    in Harandar, Slayer's Rise and Voidstorm on one account, so a low id matches
    more than one zone, and a name bound to the wrong zone is worse than a name
    that does not resolve at all: the first sends people to another continent and
    the second only loses a report. The addon collects the evidence and a human
    weighs it.
    /ewc comm prints each unreadable name with its shards, so one line can be sent
    on when the sample is conclusive.
  • Measure the claim from the last sighting, not from when we got round to looking
    The linger store has been empty since it was added, and it is the reason a
    claimed crate says ON THE GROUND, OURS with no countdown beside it: the
    countdown waits for three readings that agree and there has never been one.
    Two constants conspired. A claimed crate's marker is not believed gone until
    LIVE_STALE, ninety seconds, because it stops being drawn before the crate stops
    being lootable -- that grace is the whole point. The claim was then required to
    be less than CLAIM_MEMORY, a hundred and twenty seconds, old. Asked from now,
    those compose: the marker had to vanish within thirty seconds of the claim for
    anything to be recorded, and every reading from thirty to a hundred and
    nineteen was dropped.
    Which is precisely the range the store exists to measure. The question it was
    added to answer is whether the marker outlasts the claim, and it could only ever
    record the cases where it barely did.
    The comment three lines below already said what to do -- dated from the last
    sighting rather than from now, because the grace is this client waiting and not
    the crate lying there. The lingered arithmetic obeyed it. The test that decided
    whether to do the arithmetic at all did not.
    No countdown appears from this commit. It appears once three readings cluster,
    and now they can be taken.
  • Name the audience a click would reach, before it is clicked
    A left-click on a row is not gated on the announce setting or on being
    privileged, and that is deliberate: the setting governs the addon speaking by
    itself, and a click is the player choosing to speak. The two gates are for two
    different things.
    Which is exactly why the tooltip has to be specific. It said "say this to your
    group", and the same click is party chat, raid chat or a raid warning depending
    on where you are and what you are -- and raid chat in somebody else's raid is
    twenty strangers reading it. Dmitrii is about to join a public raid on a clean
    profile to watch the sync work, so "your group" is the wrong amount of detail.
    One function decides the channel now and returns the wording with it, because
    the tooltip promising one audience while the send reaches another is worse than
    either alone. tools/firstrun.py drives all four group states and compares what
    the tooltip would say against the channel the send actually used; hardcoding
    the send to PARTY was watched failing it before this was committed.
  • Ask the map question by its real name, and let a command say when it broke
    C_Map.CanSetUserWaypoint does not exist. The function is
    CanSetUserWaypointOnMap, and the wrong name has been in this file for as long
    as the file has. It went unnoticed because of where it sat: the happy path in
    SetCratePin stopped asking permission months ago, so the only surviving callers
    were the branch that explains why a pin did not take, and /ewc pin. Pins kept
    taking, so the explanation never ran.
    Which means the branch written to say why nothing appeared would itself have
    thrown, and /ewc pin -- the diagnostic that exists precisely because a pin
    failed twice with nothing on screen to say why -- died after two lines. Twice,
    on the clean profile, and the only trace was a truncated log.
    Asked by its real name now, and guarded: nil when the client will not answer,
    so the failure line says it cannot tell rather than throwing. A diagnostic that
    throws is worse than one that admits it does not know.
    The wider fix is the dispatcher. Every /ewc command now runs under pcall and a
    broken one prints which command broke and where. That is what should have been
    on screen instead of two lines and silence, and it holds for the next API that
    gets renamed under us.
  • Take the release correction from the zone, not from the average of all of them
    The correction was pooled across zones, on the stated grounds that the cause is
    not zone-specific -- an average speed read over the fit window against a
    transport that slows on approach would not care which zone it flew over.
    98 readings say it does care. Slayer's Rise runs +42s, Voidstorm +33s, Harandar
    +30s, Eversong +21s and Zul'Aman +18s, and the pooled figure every one of them
    received was +26s. Zul'Aman's own 26 readings span ten seconds, so its mean is
    well determined and it was being dragged eight seconds wide by zones it has
    nothing to do with. Slayer's Rise was sixteen short in the other direction.
    A zone's own readings now win once it has BIAS_MIN_N of them, and pooling is
    the fallback for a zone that has not been timed enough to stand on. ETA already
    carried the zone id, so nothing above it changes.
    The spread travels with the figure, so a zone whose readings disagree still
    says so rather than presenting a countdown as precise: Zul'Aman reports a
    ten-second spread and Voidstorm a seventy-three.
  • Say where the pin went once, then move it quietly
    Only the vignette path checked whether the pin had actually moved before
    announcing it. The two prediction paths pinned on every sweep and said so each
    time, so one transport that held a single answer for a minute printed six of
    those lines for two decisions -- and two of the six had no decision line near
    them at all, because nothing had changed.
    Where the pin went is on the line above it either way. All three automatic
    paths now go through one gate, which says it once per zone and then moves the
    pin without narrating. A landing still re-pins however small the drift: that is
    the one position nobody had to guess at.
    /ewc pin and announcing stay loud. Both are things the player just asked for.
  • Fix /ewc route, and catch the shape of mistake that broke it
    Route.Plan grew a shardOf parameter ahead of its now. UI/Model.lua was updated
    and Core/Commands.lua was not, so /ewc route went on passing four arguments
    and the timer it then tried to call as a function was the number now.
    Lua does not check arity, so nothing saw it. The suite was green, the deploy
    gate was green, and the command was dead for anyone who had set a route --
    which is the same command, so /ewc route ZA ES followed by /ewc route was
    enough. Dmitrii's route is empty, the loop never ran, and he never hit it.
    The lint now reads every ns.Module.Func call against its definition and reports
    a bare name sitting in a parameter of another name. Arity alone cannot do this:
    trailing arguments are dropped deliberately all over this addon, and Phase
    .Recall is called with six of its seven on purpose. A name at the wrong
    position is different -- it is what a call looks like after a signature grew in
    the middle and the caller did not.
    It earned itself immediately: fixing the first one with a blanket substitution
    put a fourth argument into Timers.Sorted, whose third is now. Green suite,
    again, and /ewc timers would have shown nothing. So the rule ignores arity
    entirely and reports the position, and both shapes were watched going red
    before this was committed.
  • Say how well the starting point was known, not just the cycle length
    The drift term answers how well a zone's cycle LENGTH is known. Nothing was
    answering how well its starting point was, and the two are independent: a
    mid-fall join looks like a falling sighting and is worth nothing like one,
    because the crate left the transport an unknown time earlier.
    Three of four descents on 24 Sep were mid-fall joins, so most of what the
    memory holds is anchored by the weakest source there is, and every one of them
    was being reported to two seconds. On the same data the yell-anchored shards
    now read five or six seconds and the mid-fall ones forty-seven to
    seventy-seven, which is the difference between a countdown and a direction.
    The back-computed sources share one figure because they share one cause: each
    subtracts an assumed descent from when the crate was found, and the readings
    run 79 to 134, so the assumption can be most of a minute out.
    A recalled phase re-enters the timers as "memory", so Absorb now skips those.
    Filing one back would relabel the mid-fall join it came from as an anchor with
    no error at all.
  • Stop counting a resample as a decision
    A track that cannot choose between two drop points narrated sixty identical
    lines in a minute on 24 Sep: same reason, same coordinates, one line a second
    for the whole approach. The change-detection was already there and the sample
    count was in its key, so every roll of the fitting window looked like news.
    The count belongs in the line, where it says how much evidence the reading
    rests on, and not in the key, where it only says the clock ticked. The same
    minute now prints two lines, one per point it actually considered.
    Also guards the nameplate unit token. A plate being torn down has none, and
    UnitGUID on nil is an error rather than a nil return -- which is how it left
    four of them in BugGrabber.
  • Show what is remembered, and what the countdown is really using
    Two things the addon now knows and had no way to say.
    /ewc phase prints the remembered cycle position for every shard, how many
    cycles it would be extrapolated across and how far out that leaves it. The
    error is the column that matters: on real data Eversong shard 1448 comes back
    two cycles old at a second, and Harandar shard 58 comes back eleven cycles old
    at eighty-eight, because Harandar's interval has never been measured on this
    client and the drift falls back to the pessimistic figure. Same memory, same
    age, and only one of them is worth believing.
    /ewc interval was still printing only the mean after GetZoneInterval stopped
    using it. Eversong reads mean 1107 against the core's 1094, so the one number
    on screen was the one number not in use. The core line now sits between them.
    Also fixes a format of a fractional second count, which 5.1 truncates quietly
    and 5.5 refuses outright -- harmless in the client, and it stopped the handler
    dead under the test interpreter.
  • Remember where a shard's cycle sits, and take the measure from the core
    Prune deletes a timer after six cycles, which is under two hours, and rightly:
    a wall of rows for shards nobody will stand in again is the whole complaint
    against RCT. But it was also the only record of where each shard's cycle sits,
    so an evening's break left the addon blind in zones it had already learned.
    Dmitrii logged in to six rows of dashes.
    Legibility is a display problem. The phase is kept separately now, absorbed
    before anything prunes, and recalled the moment this client stands in a shard
    it has stood in before. On his own data every one of eight remembered shards
    comes back, including one nine cycles old.
    What degrades is the arithmetic, not the anchor. Extrapolating across n cycles
    multiplies the interval's own uncertainty by n, so Recall computes that error
    and refuses once it passes what an answer is worth. A recalled phase is ranked
    where nothing seen since can be overruled by it.
    Which exposed a worse problem. The first drift estimate used the full range of
    the observed gaps, and range is set by the worst pairing in the pile and grows
    wider the more readings arrive -- so more evidence made the addon trust itself
    less. Zul'Aman came out at nineteen seconds a cycle on gaps whose core spans
    eight, and every remembered shard was refused.
    The same fault ran through the interval itself. GapStats averages everything,
    which is right for weighting a four-cycle observation as four times the
    evidence and wrong for ignoring a bad pairing. Eversong reads 1087 to 1095 ten
    times and then 1137, 1151 and 1183: its mean was 1106 and its core is 1094, and
    the mean was what the countdown used.
    So both now come from the densest run of readings that agree -- the estimator
    the descent has used since the day Eversong reported sixty-four seconds -- and
    that estimator moves to Core/Init.lua where the three modules that need it can
    share one copy. Drift is then the standard error of the figure in use, so it
    falls as readings accumulate instead of rising.
    On his saved data: Eversong 1106 to 1094, Zul'Aman 1092 to 1095, and the drift
    from nineteen seconds a cycle to seven tenths.
  • Stop the addon talking over itself, and say what it is refusing
    Four things, all of them the addon being noisier or vaguer than it knew.
    A crate under its parachute drifts, and the pin followed it at a hundredth of
    a percent of the map -- so every sweep counted as a new place to point at, and
    said so. Forty identical lines in eighty seconds. The pin still follows, now at
    four tenths of a percent, and only the first placement is worth a line. A
    landing always re-pins whatever the drift, because that is the one position
    nobody had to guess at.
    A descent reading refused as partial said only "joined mid-fall", and three
    different checks can refuse one. They are not equally likely to be right: one
    of them is this addon's own re-shard guard, added days ago and never tested
    against a live case. A fall watched from three tenths of a percent away came
    back flagged, which is the shape of that guard misfiring. The reason is
    recorded now and printed, so the next one says which check did it.
    Announcements carried coordinates beside a map pin, which is the same fact
    twice. Time replaces them -- and the state that carried no time at all was the
    one where time decides the answer: a raid will cross a zone for a crate that
    landed five seconds ago and not for one that landed two minutes ago.
    And the deploy script would ship Lua that does not parse. It checked that the
    files matched the .toc and never that they were readable, so a file with an
    unfinished string went into the live AddOns folder and the only thing that
    would have noticed was the game, by silently not loading the addon. It refuses
    now. Shown red against a deliberately broken file and green again after.
  • Say a row to the group, and say whose the crate was
    Left-click a row and it goes to the raid: what is on the ground, what is
    landing, what is in the air, or the two countdowns. With a clickable pin when
    there are coordinates, which costs moving your own waypoint, because the only
    link chat keeps clickable is the one made from it.
    Gated far more loosely than the automatic announce and on purpose. That one
    fires by itself on every client running this addon, so it is held to the
    leader; this is one person choosing once, so a raid warning is fair where the
    game allows one. What arrived as an RCT raid warning is refused, though --
    the raid has already read it, and echoing it back is spam dressed as help.
    The faction half. The game draws only your own side's claimed marker, so
    seeing either claimed id means your side took it, whichever one arrived. The
    6067-Alliance, 6068-Horde table is a guess: WarCratePredict shipped it from one
    player's sample and had it contradicted live, announcing "claimed by Horde" on
    a Horde character while an Alliance player was watched looting the crate. The
    faction is read off the player instead.
    Which gives the useful half for free: a crate that vanishes with no claim of
    ours drawn, in a zone that did not re-shard, was taken by the other side. That
    is the case worth naming, because the window would otherwise go on saying ON
    THE GROUND to somebody about to cross a zone for nothing.
    And the opposite case, which Dmitrii watched happen: a crate claimed by your
    own side is still there to loot. The marker flags which side captured it, not
    that anybody carried it off -- WarCratePredict says the same, and it is why
    their claim used to fire at drop time. Clearing the row on that marker told the
    player it was over while they could still go and take it. It now stays, and
    says it is yours.
  • Date a crate found on the ground back to when it spawned
    Finding one lying there seeded the timer at the moment somebody noticed it,
    which is late by a flight and a fall every single time -- around two and a half
    minutes on the readings to hand.
    Both legs are measured now, so the known part of that error comes off. What
    cannot be known is how long it lay there before anybody looked, so the reading
    is still not called precise: this removes the part of the error that is known
    and leaves the part that is not. RCT does the same and calls it a spawn offset.
    Refused unless both legs rest on readings rather than the shipped guess. Built
    out of two guesses the correction would be a guess in a correction's clothes,
    and it would move every timer without anybody having measured anything. Shown
    red against a build with that check removed.
  • Read RCT's own traffic, without bundling anything to do it
    Their payload is AceSerializer, then LibDeflate, then EncodeForPrint -- their
    Sync/wire.lua, read from the copy on disk. Nothing is encrypted. The signature
    beside it is theirs to check rather than ours to satisfy: reading a broadcast
    is not the same as claiming to be one of them.
    What stopped this before was weight. Between them the two libraries are most of
    the weight this addon exists without. They are borrowed instead: LibStub hands
    out whatever any installed addon has already loaded, and both are everywhere --
    Details, BugSack and several more carry them. With neither present the message
    stays undecoded and says so, exactly as before.
    Their capture states map onto ours, including Monster Say, which is their
    announcer catch and means a crate is in the air. A state they do not name
    becomes an anchor, ranked as weakly as it deserves. A spot with no zone, or
    with their shard reading "N/A", is refused rather than turned into a report
    with nothing to key it by.
    Reassembly matters more than it looked. AceComm splits anything over a packet
    and RCT's bulk sync carries its whole database: 145, 255 and 255 bytes from one
    client in one burst. Refusing split messages threw away most of what there was
    to read. The framing is handled in the pure module so the ordering, two senders
    interleaving, joining mid-sync, a stalled run and an endless one are all under
    test.
    Token traffic is counted rather than logged. One client sent seventeen requests
    in ten seconds and two hundred and thirty-four inside a couple of minutes,
    which would have pushed every real sighting out of a twenty-line log before
    anybody could read it. The count still proves the channel is alive, which is
    all a handshake was ever evidence of. TOKEN_REQ carries no payload at all and
    was being filed as "not theirs" until it arrived nine bytes at a time.
    /ewc comm now also checks that each prefix is registered rather than assuming
    it. A client caps how many prefixes every addon may hold between them, the
    refusal is silent, and without the check "nobody is broadcasting" and "we never
    subscribed" read identically.
    Also here, because Comm is the transport for it: a row clicked in the window is
    sent to the group.
  • Say where a row's information came from, and whose it is
    Two things the window was saying that it did not know.
    A zone with no timers at all read "new shard", which blames the copy of the
    zone when the zone itself has never been watched. Dmitrii saw it in Harandar,
    where nothing has ever been timed in any copy. They are different news: one is
    a gap in what the addon knows, the other is a fact about right now. A zone is
    only a new shard if it has been timed somewhere else.
    And a crate somebody else reported looked exactly like one this client saw.
    That matters more than it reads: a report is about the copy of the zone THEY
    are standing in, so flying to it is a decision taken on somebody else's word.
    Those rows carry a dot now, and the tooltip names who said it and which addon
    carried it.
    Checked against the real saved variables rather than by reasoning: an RCT raid
    alert for Zul'Aman, decoded while standing in Harandar, floats Zul'Aman to the
    top of the window as inbound and supplies its shard.
  • Show the timer for the copy of the zone you are actually in
    A timer belongs to one shard. The window was showing the freshest entry for a
    zone whatever shard it came from, so after re-sharding it presented another
    copy's countdown as yours. Three states now, and they say different things: a
    timer for the shard you are in, "new shard" when the shard is known and nothing
    has been timed in it, and a marked guess when the shard is not known at all.
    Blank is the point. Another copy's countdown in that place looks exactly like
    knowledge, and a raid flies out on the strength of it and finds nothing. This
    is the commonest complaint in crate farming and it has nothing to do with the
    crate failing to spawn.
    Knowing the shard therefore matters more than it did, so it is read from
    anything available: any vignette rather than only a crate's -- a Renown
    Quartermaster answered it in Eversong -- nameplates already on screen rather
    than only newly added ones, and a target, mouseover or pet.
    And from the raid. A player standing in a zone now reports which copy they are
    in, which is the one thing nobody can find out about a zone they are not in. A
    scout parked in Zul'Aman knows its shard; everybody else finds out on arrival,
    which is too late to have chosen. Their report answers for that zone until
    this client has its own reading.
    Last: a crate that stops being drawn while you stand in its zone is reported,
    and the line says whether the shard moved under you. Dmitrii watched one land
    in Slayer's Rise, flew over and found nothing, and an ordinary loot and a
    re-shard look identical from a distance. The claimed vignette separates them,
    and its absence is the interesting case.
  • Answer when the crate lands, and stop guessing when to leave
    The window said "drop" over a countdown that was really the spawn -- the moment
    the transport appears -- and "leave" over advice nobody asked for. Now it says
    both of the things a farmer is deciding between: when the transport arrives,
    and when the crate is on the ground.
    The second needs the first leg of the flight, which was computed from the
    fitted speed and ran +16 to +64 seconds out depending on the zone, because the
    transport slows on approach and circles before letting go. An average speed
    cannot know that. It is timed now, per zone, on the same estimator as the fall:
    the median of the densest run of readings that agree.
    Only an announcer anchors it cleanly, because only an announcement is really
    the spawn. Catching the transport in the air says when it came into range,
    which is a lower bound by however long it had already been flying; those are
    kept, flagged, and left out of the figure, exactly as a parachute joined
    mid-fall is. So this fills slowly, and the column is provisional until a zone
    has three readings that agree.
    Travel times go with the leave column, and so does everything they touched:
    the field in Data/Zones.lua, GetZoneTravel, db.travel, /ewc travel, the
    planner's leaveIn, and the go and missed statuses that were only ever derived
    from them. They were estimates by their own admission, and which drop point a
    crate picks moves the number as much as which zone does. It was advice wearing
    a number.
  • Write down what this is, for anyone who is not me
    There was no tracked documentation at all. The brief and the open-threads list
    had just moved under tmp/ and out of git, so a clone carried the addon and
    nothing that says what it does or why it refuses to do certain things.
    The README covers the features, every slash command, and a section on what it
    deliberately does not do, which is the part that explains the design: it will
    not tell you when to set off, it will not keep strangers' data on disk, and it
    will not present a guess as a measurement.
  • Say when a countdown belongs to a different copy of the zone
    The commonest complaint in crate farming, whichever addon people run: the raid
    flies to a zone the timer says is due and no transport comes. It is not that
    the crate failed to spawn. A timer belongs to a shard, entering a zone hands
    you one you did not choose, and the crate on the shard you landed on is simply
    at another point in its cycle.
    The wiki is explicit that group members are pulled to the leader's shard only
    when joining and only when already in the leader's zone, so flying somewhere
    together guarantees nothing. Entering or leaving a group, toggling War Mode or
    Chromie Time all move a player, and players on the Blizzard forums report the
    same after a battleground. Nothing about offline members, which was the other
    theory going around.
    So the row now says it. A countdown learned on a shard other than the one this
    client is standing in is marked apart from the ~ that means "seeded from a
    crate on the ground", and the tooltip explains it in words. Three specs,
    including that not knowing the shard is not a mismatch: silence is not evidence
    of being in the wrong place.
    Knowing the shard is what makes that possible, and it now comes from anything
    available. Any vignette at all, not only a crate's -- a rare elite standing
    about answers the question, and Voidstorm's elites read 207300 beside a
    creature GUID saying 207300. Nameplates already on screen are read directly
    rather than waited on, which covers the case that caught this out: after a
    reload the plates are up but the event that announces them fired before the
    addon loaded, and an announcer spoke into exactly that gap.
    Asking the game to target something would be the obvious way and is not
    available: TargetNearestEnemy and its relatives are protected and run only from
    a keypress.
    A descent reading is also refused now when the zone's shard changed while it
    was falling. The check that decides whether a fall was watched from the start
    is kept per zone while the reading is keyed per zone and shard, so a re-shard
    made "this parachute was not there a moment ago" trivially true and recorded
    half a fall as a whole one.
  • Wire the evening's work into the scanner, the commands and the load order
    The plumbing for the four changes that landed beside it. Kept together because
    the same four files carry all of it and splitting them further would mean
    commits that never built.
    Scanner learns which shard this client is standing in, from a crate vignette or
    from any creature the game will name. The announcer needs it: the chat event
    for a monster's speech carries no GUID at all, not a secret string but nil, so
    the speaker cannot supply it. A creature and a vignette give the same number,
    which /ewc shard settled after an hour of doubting it.
    Scanner also measures how far the player stood from each landing, and the
    announcement handler reports whether a timer was actually anchored rather than
    whether a phrase matched. The first live announcement said "anchored the timer"
    having anchored nothing, which is the worst kind of diagnostic.
    Main keeps a log of everything this addon says, in the saved variables.
    /chatlog does not capture it: that records the CHAT_MSG_ stream, and an addon's
    print goes straight to the frame through AddMessage without ever becoming a
    chat message. WarCratePredict keeps its own log for the same reason. It costs
    a /reload to read and misses nothing.
    Main also reports a sighting to the group and offers the group's own reports a
    place in the timers, both guarded: everything below those two lines is the
    addon's core work, and a nil call there would stop all of it without a word --
    no pin, no prediction score, no release measurement, no learned spot.
    Commands gain /ewc data, /ewc yells, /ewc comm and /ewc announce, and the
    airtime readout now shows how far away each reading was taken from.
  • Take the descent from the readings that agree with each other
    The median could not survive the data. Eversong held 14, 43, 84 and 92, so its
    median was 64 -- a time no crate there has ever taken -- and the countdown was
    being driven by it. Slayer's Rise read 103 the same way.
    The spread is not the fall varying. Zul'Aman measured 85s and 44s at 49.0,
    69.3, and Slayer's Rise 91s and 134s within half a percent of one spot, so the
    same crate landing in the same place reads forty seconds apart. Splitting the
    figure per drop point, as RCT does, would not have helped.
    Both tails have one mechanism seen from opposite ends: a parachute noticed late
    reads short, an on-ground vignette noticed late reads long. Set them aside and
    every zone measured so far holds a cluster near 86.
    So the figure is now the median of the densest run of readings within ten
    seconds. Fewer than three readings keeps a plain median, because two cannot be
    shown to disagree. A zone with several that do not agree borrows the cluster
    from every zone pooled and says so, in the readout and on the prediction line,
    rather than presenting a borrowed number as a measured one. On the readings to
    hand every zone lands on 86, including the two that were reporting 64 and 103.
    The estimator finds agreement, it does not assume the answer: a spec pins a
    cluster at 122 being reported as 122.
    Each reading now records how far the player stood from where it landed. That is
    the instrument for the question this leaves open -- whether a distant parachute
    is simply drawn late -- and it needed the argument tail to become a table,
    which the function's own comment had already asked for.
  • Take what other players and other addons can already see
    A raid is spread across zones and the information exists; it just never reaches
    the person deciding where to fly. Everything here is learned by sitting through
    cycles, so one player watching one zone is blind everywhere else.
    What is decoded, in the order it is worth anything:
    RCT announces a flying crate to raid chat, and assembles that line in English
    regardless of locale, so the frame around the two values is fixed. Read as
    ordinary chat with nothing decrypted and nothing installed. Worked live on
    20 Sep, four times in an evening, each one improving a timer this client would
    otherwise have had to guess at. Only the zone name inside it is localised, so
    zone names are matched from C_Map on this client.
    WarCrateTracker broadcasts SPOT_V2 in the clear to guild and party, carrying
    the same vignette ids this addon already reads.
    RCT's own prefix and CrateTrackerZK's are registered and logged but not
    decoded: their payloads are serialised and deflated, and reading them means
    bundling LibDeflate and AceSerializer, which is most of the weight this addon
    exists without. HGLog, which RCT bundles, shares the same anchors in the clear
    on HGLOG1, so there is nothing behind the compression worth the libraries.
    Remote data is not local data, and that is the whole complaint against RCT: it
    takes every sighting from every group and guild member into its saved database
    and never cleans it, so an evening leaves a wall of rows for shards nobody will
    stand in again. Here a report lives while the raid does, carries who sent it,
    and never reaches disk. It moves into db.crates at one moment: when this client
    confirms the crate itself. Promoting it then is not sentiment, it is that a
    scout who caught the transport holds a precision-3 anchor while this client
    arriving to a crate on the ground holds a 1.
    An HGLog row is an anchor, not a sighting, and has its own stage so that a
    fifteen-minute-old row from somebody's database can never be drawn as a crate
    in the air. Seeing the crate ourselves retires what we were told about it: a
    report outlived its crate once and the window went on offering "inbound" for a
    zone that was finished with.
    The receive gate is the one WarCratePredict had to add after the fact. A
    whisper is never a legitimate transport, or anyone on the realm can hand you a
    crafted message and move a timer with no group, guild or acquaintance.
  • Anchor the cycle on the NPC who announces it
    The line is scripted to the spawn, so it dates the timer as well as catching
    the transport does and arrives earlier, before the transport is near enough to
    draw a vignette. That is what should make Eversong and Voidstorm work, where it
    is still out of range when the cycle starts. It also needs no addon on anybody
    else's machine, which makes it the cheapest source of data there is.
    The name gates the phrases and both are required. Without the gate a boss taunt
    anchors a cycle, which WarCratePredict hit as Decimus saying "I hunger for the
    OPPORTUNITY"; without the phrases an announcer's idle line does. Ranked with a
    flying catch, because that is what it is.
    Every locale is matched at once instead of switching on GetLocale, since the
    names are localised too and the gate holds either way. Russian names and
    phrasings come from CrateTrackerZK's ruRU locale.
    Lua's string.lower only touches bytes below 128, so a Cyrillic phrase lowered
    and compared never matches. Matched against the raw text as well, with a spec
    that fails without it. That spec is also what exposed the test runner: Windows
    hands stdout a cp1252 encoder, so a failure message containing Cyrillic took
    the whole run down with a traceback instead of naming the test that failed. A
    suite that cannot report a failure is worse than one that has none.
    Heard live on 20 Sep: Vidious in Voidstorm and Ziadan in Zul'Aman, both
    matched. The shard is a separate problem and is handled in its own commit.
  • Let the player see and correct what the addon has learned
    A bad reading could only be removed by typing /ewc airtime drop HA 5, which
    means reading an index out of a chat log. Evidence that can only be corrected
    from a command line does not get corrected, and this addon exists because RCT
    accumulates data nobody can clean.
    Six lists behind one button in the settings panel: timers per zone and shard,
    descent readings, observed intervals, drop points learned beyond the shipped
    catalogue, the route, and travel overrides. Any row can go; a zone or a whole
    list can be cleared behind a confirmation.
    Every row carries a stamp of the record it stands for, and Remove refuses when
    the stamp no longer matches. A row holds an index into a list that keeps
    growing while the panel is open, so a descent reading arriving between painting
    a row and clicking its button would otherwise delete a different record than
    the one on screen, silently. Three specs cover that and were shown red against
    a build with the guard removed.
    The panel does not tick. The timers list sorts by how soon each drops, so a
    one-second repaint would reorder rows under a cursor on its way to a remove
    button. A stale countdown in a management panel costs nothing; deleting the row
    below the one you aimed at costs a measurement.
    UI/Manage.lua decides every row and is covered by tests; UI/DataPanel.lua only
    paints them. The settings panel itself is now switches only, in four sections.
    Anything that is a list of records lives behind the button, because that API
    draws checkboxes and sliders and has nowhere to put a row with its own delete.
    RCT puts 39 controls on one page, including the warning frame's font and a
    donation link, and the three settings that change how it farms are lost there.
  • Stop tracking the working notes and the local tooling
    TODO.md and PROMPT.md are how this project thinks, not part of the addon, and
    they sat in the repository root where publishing would carry them along. They
    move to tmp/, which is ignored. Worth knowing: git mv would have tracked them
    there anyway, because .gitignore has no effect on a file already in the index.
    tools/ goes the same way. deploy.py targets one machine and loglens.py reads
    that machine's game folder; neither is anything a stranger cloning this could
    use. A fresh clone therefore has no deploy script, which is the accepted cost.
    tests/ deliberately stays: there is nothing local in it and it is the part of
    this project most worth reading.
    .pkgmeta covers the other meaning of publishing. deploy.py already copies the
    .toc and exactly the files it lists, so the deployed folder is precisely what
    the game loads; this makes the packaged zip agree with it under the same rule
    rather than under a second list kept in step by hand.
    The notes remain in the history. Publishing the repository would expose them in
    old commits, and rewriting history to change that is a separate decision.
  • Report the middle descent, and stop trusting a parachute that drifted into view
    Two faults, both visible in one night's logs.
    Eversong recorded 14 seconds as a complete descent. The crate had been falling
    before the player arrived; two sweeps of the zone had already happened by the
    time its parachute came into vignette range, so "it was not here a moment ago"
    was satisfied by a crate that had been in the air the whole time. A vignette
    entering range and a vignette appearing are identical from here.
    The only unambiguous evidence is having watched the transport reach its drop
    point, which is the moment the crate was let go, and that now fires reliably
    because the prediction commits within five samples. Failing that, a fall is
    only trusted if the zone had been under observation for longer than any
    possible fall before the parachute showed.
    And the figure itself is now the middle reading rather than the mean. Harandar
    has measured 86, 86, 87, 61, 19, 86, 86: five of seven agree, and the mean is
    73, which is not a time any crate there has ever taken. Both tails are genuine
    -- a crate can catch on a branch and be down early, and the game can leave the
    parachute drawn for half a minute after it lands -- so no correction removes
    them and only the middle survives them.
    Three tests, all red against the mean.
  • Translate a fallback position instead of passing it off as zone coordinates
    A vignette position comes back in the space of the map it was asked about.
    When the zone map yields nothing the lookup falls back to the player's own map,
    and two of its three callers dropped the second return value -- so those
    coordinates went to the catalogue, the prediction and the waypoint as though
    they were zone-map coordinates. Standing in The Den, a sub-zone of Harandar,
    that is a different place entirely.
    The fallback now round-trips through world coordinates into zone space. If that
    conversion fails the reading is refused rather than used, and the two tracking
    callers check which map they were handed either way. Only /ewc scan may print a
    position from elsewhere, because it names the map beside it.
    Found while working out why a transport crossed Slayer's Rise untracked. That
    is not explained yet -- both vignettes in that scan had no position on any map,
    which the previous commit made audible -- but this was in the same lookup.
  • Say when a crate cannot be placed instead of skipping it in silence
    A scan in Slayer's Rise returned two vignettes the game would give no position
    for. They were War Mode Quartermasters, which this addon does not track, so
    nothing was lost -- but it showed what happens when the lookup fails: the
    vignette is skipped by an "if pos then" with no else, and nothing is said.
    For a quartermaster that is correct. For a transport or a crate it is the
    addon going blind and reporting success, which is the exact failure it exists
    to prevent. A tracked stage with no resolvable position now says so, once a
    minute per zone rather than once a second.
    No fix to the lookup itself: it already tries the zone map and then the
    player's raw map, and a vignette the game will not place on either is a
    vignette with nowhere to be.
  • Fix the clock mismatch that flagged every descent as a fragment
    The sweep marker was stored as tNow -- GetTime(), seconds since the client
    started -- and compared against stamp, a unix timestamp from GetServerTime().
    About 1.7 billion between them, so "did we sweep this zone recently" was never
    satisfied and every descent reading in every zone came out a fragment. Slayer's
    Rise was watched from transport to parachute and still lost its reading.
    The audit of the rest of the file is clean: tNow only meets recentDrop, spotted
    and tr.lastSeen; stamp only meets fallingSince, arrivedAt and atlasFlip.
    The suite cannot see Scanner.lua at all, so the only check this had was a
    player reading a log. The linter now carries the rule: it learns which tables
    hold which clock, follows that through multiple assignment and into the locals
    that read them, and refuses arithmetic across the two.
    Both earlier drafts of the rule were green against the live bug -- one matched
    only single assignment, the other never reached the local where the comparison
    sits. A check that has only ever passed has not been tested, so this one is
    shown red, restored, and green again.
  • Call the drop point a circling transport is already sitting on
    Flying into Zul'Aman on a fresh shard found the transport orbiting its drop
    point. The addon held six samples of it and said "not far enough yet to read a
    heading" -- refusing to answer while the answer was directly underneath.
    A track with samples but no baseline is not a track with no information. It is
    a transport that has arrived. Its mean position, snapped to the nearest
    catalogued spot within 3% of the map, is the drop point; no ray and no bearing
    are involved, so none are computed. It commits, prints where and how long the
    crate will take to come down, and pins it.
    This is also the case with the least warning -- the crate can leave at any
    moment -- so it is the one where saying nothing costs most.
    Five tests. The snap radius is load-bearing: widened, the "circling nowhere
    near a catalogued spot" case goes red.
  • A parachute joined mid-fall must not anchor the cycle timer either
    Zul'Aman reported a 989-second cycle on shard 97 against a true figure near
    1090. Both ends of that gap were crates already under their parachutes when the
    player arrived: each timestamp is late by however much of the fall had already
    run, and the gap carries both errors.
    The addon already knew both sightings were fragments -- it printed "joined
    mid-fall" for each -- and told the timer nothing. A parachute picked up partway
    through says only that a crate spawned some minutes ago, which is exactly what
    finding one on the ground says, so it is ranked with it. It still refines to a
    proper sighting when one arrives, and a parachute actually seen to leave the
    transport still measures the cycle.
    Three tests, two red against ranking it as a real falling sighting.
    /ewc interval now numbers its observations and takes drop ZONE N and
    reset ZONE, the way /ewc airtime does. The 989 is already recorded.
  • Judge a descent by whether we watched it start, not by a heading fit
    The previous gate threw away good readings. Slayer's Rise was watched from
    "transport spotted" through commit to the crate appearing, and Voidstorm
    produced 86 seconds -- the most accurate figure in the whole set -- and both
    were discarded as fragments.
    The gate asked whether the track had reached its drop point, and that needs a
    live heading fit. A transport circling its drop point has no baseline to fit,
    so Evaluate returns early and arrived is never set. Neither log carries a
    "transport reached" line; the flag simply never fires in the case it was meant
    to certify.
    The question is now asked directly: had we swept this zone in the last minute
    without this parachute in it? If so the fall began while we were watching. A
    parachute present in the first sweep of a zone was up before we arrived, and
    Scanner.Reset clears the sweep record on every zone change, so flying in
    mid-fall cannot look like anything else.
    Also fixed: flip was computed from fallingSince one line after it was set to
    nil, which is arithmetic on nil. It has not fired yet only because no crate has
    changed its art, so the first one to do it would have thrown.
    The overlap flag is gone from the descent report as well. Nineteen readings out
    of nineteen carried it.
  • Watch the parachute's art for the landing the vignette id hides
    Dmitrii: a crate can catch on a branch and reach the ground early, so a short
    reading is not automatically a broken one -- which retires the advice to delete
    them. The joined-mid-fall flag is the only honest discriminator and it exists
    only for readings taken since it was added.
    And: a landed crate can keep its parachute drawn for 30 to 54 seconds. The
    unexplained excess in the long readings is 44 and 43 seconds, inside that range
    at both ends, so the ground vignette arriving late is very likely the whole of
    it.
    If the id stays 2967 through that but the art changes to the crate, the real
    landing is observable. Each fall now records whether atlasName changed and how
    far in, so /ewc airtime can show it against the reading. Nothing acts on it
    yet: this establishes whether the signal exists.
  • Measure the circling leg, because the fall does not explain the spread
    Nineteen readings, and the means are not trustworthy: Harandar sits at 86-87
    four times over, Eversong at 84, but Zul'Aman splits 83/85 against 129/129 and
    Slayer's Rise gave 91 and 134 at what is effectively one spot -- 0.4% of the
    map apart. Terrain cannot explain two readings 43 seconds apart in the same
    place, so the elevation theory for Zul'Aman is dead.
    Two observation-loss theories are dead too. Leaving a zone fires
    ZONE_CHANGED_NEW_AREA, which calls Scanner.Reset and clears the fall marks
    outright, so a reading that survives a zone change does not exist. And the
    overlap flag -- the parachute still drawn when the crate is found down -- fired
    on 19 readings out of 19, which means the parachute was visible at every
    landing and sight of it was never lost. It also means the flag separates
    nothing, so it is no longer printed.
    What has never been measured is the leg between the transport reaching its
    point and the parachute appearing. The release estimate already runs 35 to 38
    seconds short in Harandar, which is the order of the unexplained spread, and
    the arrival is now a known moment. Each reading records how long the transport
    circled first, and /ewc airtime prints it. If the long falls carry long circles
    it is release timing; if they do not, the fall really does vary.
    That is instrumentation, not a fix. Two guesses have already failed here and a
    third would not be worth more than the measurement.
  • Let one bad descent reading be dropped without losing the zone
    A reading costs a full crate cycle to obtain -- about eighteen minutes -- so
    clearing a whole zone to remove one mis-paired figure is an expensive way to
    fix it. Harandar holds five good readings and a 19; Zul'Aman four and a 44.
    The listing is numbered and /ewc airtime drop ZONE N removes that one.
  • Pin the crate you can see, not only the one you predicted
    Flying into Zul'Aman with a crate already under its parachute left the map
    bare. The pin was only ever placed from a prediction, and there is nothing left
    to predict once the transport has dropped -- so the one position that needed no
    guessing at all, printed on the line above it, was the one position never
    pinned.
    An observed crate now sets the pin, overriding whatever the prediction put
    there, and holds it until the crate moves or is claimed.
  • A fall counts as measured only if we watched the transport arrive
    Harandar's descent mean read 71s across six readings spanning 19 to 87, against
    a true figure of roughly 86 that four zones agree on. A 19-second fall is not
    physical; the reading was a fragment counted as a whole.
    The test for "we saw this crate leave the plane" was "is a track open in this
    zone", which is a different question. A transport keeps circling after it
    drops, so flying into a zone mid-fall shows you one, opens a fresh track, and
    qualifies your fragment of a fall as a complete measurement. The guard meant to
    suppress those tracks keys off recentDrop, and recentDrop is only set once a
    crate has been seen -- so on the first sweep after entering a zone the outcome
    comes down to whether the transport's vignette is listed before the crate's.
    That is why it corrupted some readings and not others.
    It now requires the track to have reached its drop point, which is the event
    that actually starts the fall. Readings from a flight we never saw arrive are
    marked partial: counted, shown, kept out of the mean.
    This runs through Detect/Scanner.lua, the one file the suite cannot reach, so
    the evidence is the code path and the 19-second reading, not a red test.
    /ewc airtime reset now takes a zone, because a contaminated zone should not
    cost the other five their history.
  • Call Eversong ES and Harandar HA, the way raids actually say them
    The displayed abbreviations came from WarCrateTracker, which writes EW and HD.
    Dmitrii's raids call out ES and HA, and since the whole point of this table is
    to match what someone says over voice, the spoken form wins.
    Nothing changes about input: ES/EW and HA/HD were all in the alias table from
    the start and all still resolve. Only the canonical form the addon writes back
    moves, which is what the route editor echoes and what the window prints.
    RCT is no help here -- it has no abbreviations at all and draws full zone
    names -- so there was nothing to copy, only a convention to match.
  • Answer as soon as there is an answer, not once the rivals fly out of range
    A Zul'Aman flight held the correct spot in first place from its fifth sample
    and said nothing for 62 seconds, committing 20 seconds before the drop. Two
    faults, one in the maths and one in the policy.
    The maths. Candidates were separated by ANGLE against a tolerance of
    SCATTER/range, which grows without bound as the transport closes in, so the
    verdict became less decided the nearer it got and could only resolve when a
    rival passed behind the plane and left the candidate list. Spots are now scored
    by sideways distance from the ray in units of what could plausibly put them
    there -- the landing scatter, plus the bearing error over that range -- and
    turned into a posterior. That sharpens on approach, which is the right
    direction. The fit's own error is floored at 0.23 degrees: the regression
    reports 0.00 for a dead-straight transport and taking that literally would
    trust a 20-second bearing absolutely.
    The policy. Zul'Aman's 48.9,69.2 and 46.9,62.3 sit on one line to within
    0.001% of the map from a northern entry. No bearing separates them and none
    ever will, so refusing to speak is refusing for the whole flight. The addon now
    names the NEAREST spot still in contention, because the transport reaches them
    in order: if the crate is there you have arrived, and if not the next is
    straight on. It says how sure it is and names what else is on the line, and it
    redraws when the evidence moves -- which on that flight is a firm call at 100%
    with 2:42 still to fall. Fanned candidates, as opposed to collinear ones, still
    produce nothing: there the near one is not on the way to anything.
    Simulated against the real catalogue and that flight's geometry, the first
    answer arrives 76 seconds earlier than it did.
    Four tests cover it, all red against the previous model.
  • Clear the old waypoint first, and stop asking permission to place one
    Two differences from RCT, whose pins do appear, and both were mine.
    RCT calls C_Map.ClearUserWaypoint before setting. This did not. An existing
    waypoint -- the player's own, another addon's, or this addon's from the
    previous flight -- can stop a new one taking, and nothing reports it.
    RCT never calls CanSetUserWaypoint. This gated on it and silently declined
    whenever it returned false, which is quietly refusing to place a pin that
    might well have worked. It now attempts the placement and reads the waypoint
    back to find out what actually happened, mentioning what CanSetUserWaypoint
    claimed only when the attempt failed.
    The second is a rule this addon states in its own comments and then broke:
    look at the artefact, not the return code. Asking an API for permission is not
    the same as finding out whether the thing happened.
  • Add /ewc pin, and stop success being silent
    The map pin has now failed to appear twice with nothing on screen either time.
    The diagnostic added for it only spoke on failure, so "no message" meant either
    that it had worked or that the player was still on an older build -- and those
    needed telling apart before anything else could be worked out. Success says so
    now.
    /ewc pin takes the question away from crate timing altogether. The prediction
    path cannot be re-run on demand, since it needs a transport in the air, so
    until now every attempt cost a whole flight to observe. This places a pin where
    the player stands and reports the setting, the map they are on, the zone it
    resolves to, whether the game permits a pin on either, whether the call took,
    and whether it is supertracked -- which is the part that puts an arrow on the
    minimap.
    One lead it should settle: the last failure happened while standing on map 2576,
    the housing area inside Harandar, with the pin being placed on 2413. That is
    correct for the coordinates, but a pin on a map the player is not looking at may
    not behave the same way.
  • Hide the headline when there is nothing to say
    It read 'no transport in the air' whenever one was not, which is the normal
    state and informs nobody, and the rows now carry the same news with the zone
    attached. What the line still adds over a row is the coordinates and how sure
    the call is, so it stays for that and takes its space back otherwise.
  • Report a transport that is there, and say why a map pin was not placed
    Two silences from one flight, both of them the addon knowing something and not
    saying it.
    The window read "no transport in the air" with one plainly in the zone. The
    prediction returned nothing whenever the heading could not be fitted, which
    conflates having a transport with having a heading for it. A heading is missing
    for the first seconds after one appears and again once it reaches its drop
    point and circles -- the beginning and the end of every flight, and both are
    moments someone looks at the window. It now reports the transport either way,
    and says which of the two it is.
    The call was made, announced and acted on, and no map pin appeared, with
    nothing on screen to say whether the addon had chosen not to place one or tried
    and been refused. It now says which, and reads the waypoint back rather than
    trusting the call -- a pin that did not take is exactly as useful as no pin.
    Also: the settings panel passed a hardcoded default of true for every checkbox,
    so "Narrate tracking" and "Verbose log" -- both diagnostics, both off by design
    -- would have defaulted on. They take their defaults from DEFAULTS now.
  • Give a zone with something happening a row, at the top
    Flying into a zone whose timer you do not have -- somewhere new, or somewhere
    you have returned to on a different shard -- produced no row at all, so the
    window was silent at exactly the moment there was something in the air or on
    the ground there. The live-crate state existed but could only be shown on a row
    that already had a reason to exist.
    Any zone with activity now gets one. A transport still in the air counts as
    activity in its own right: it is the reason to stay put, and until now the only
    sign of one was a line of text that says nothing about which zone unless you
    happen to be stood in it.
    Live rows float above the countdowns, most urgent first -- a crate on the
    ground can be taken now, one under a parachute shortly, a transport eventually
    -- while everything else keeps the order it was built in.
  • Do not average a descent that was joined halfway down
    Flying into Slayer's Rise while a crate was already on its parachute produced a
    reading of 115 seconds -- from the moment the player arrived to the landing,
    with no way to know how long it had been falling before that. The real descent
    is longer by an unknown amount, so the figure is a lower bound rather than a
    measurement, and averaging lower bounds with complete readings pulls the answer
    down every time it happens.
    Whether the whole fall was seen is knowable: it depends on whether the
    transport that dropped it was being tracked when its parachute appeared. That
    flag is now captured before the track is torn down, and flagged readings are
    counted and displayed but excluded from the mean and the range.
    This matters right now because Slayer's Rise and Zul'Aman are the two zones
    whose descents look unlike the other four, and 115 was about to become evidence
    for that when it is not evidence of anything.
  • Record where each descent was measured, and stop calling a new track "circling"
    Zul'Aman has now produced descents of 83 and 129 seconds where four other zones
    sit inside 84 to 87. That is not measurement noise, it is the zone behaving
    differently, and elevation is the obvious suspect: lower ground under the drop
    point means a longer fall, and Zul'Aman has a temple complex above a jungle.
    Both 129s came from the same spot.
    Which would mean splitting the descent by drop point after all, as RCT does and
    as this deliberately did not. It cannot be settled yet, because the readings did
    not record which spot they came from -- so that is what changed. Instrument
    first, decide once the data can answer.
    Also: "it has stopped moving -- circling its drop point" was printed two seconds
    after a transport was first spotted, when it had not moved far enough to read a
    heading yet. Same condition, opposite meaning, and the track already knows which
    it is by whether it has ever fitted.
  • Keep a crate that is down or falling on the window while it lasts
    The timer resets the instant a crate is released, so the row jumped from 0:05
    to 18:10 at exactly the moment there was a crate lying there to go and collect.
    The countdown was right and useless.
    A crate in the air or on the ground is now tracked per zone, separately from
    the timer, because they answer different questions. Its row shows ON THE GROUND
    or a landing countdown in place of the wait for the next one, with the bar
    filled, and the tooltip says which. The timer is still underneath -- the next
    drop has not stopped mattering.
    Cleared when the crate is claimed, when its zone is left, or after ninety
    seconds without seeing it while standing there, since by then it has been taken
    or was never really there.
    Also removed Scanner.FallingSince, which had been dead since descents started
    being keyed by zone and shard rather than zone alone.
  • Label the window's columns and explain a row on hover
    The first person to see the window asked what the two countdowns meant and
    what "x2" was. That is the answer about the design, not a question to answer in
    chat: three unlabelled values on one row is not a readout.
    There is a column header now -- and it drops the leave column when no route is
    set, since without one there is nothing to leave for. Hovering a row gives the
    zone, the shard, both countdowns in words, the travel time behind the leave
    figure, and what the tilde and the missed cycles actually mean.
    The missed count came off the row. It was an unexplained "x2" beside two
    unexplained countdowns, and the dimming already says "do not trust this one"
    without needing jargon; the number itself is worth having, so it moved to the
    tooltip where there is room to say what it implies.
  • Say why a heading cannot be fitted, instead of blaming the sample count
    A live flight in Eversong reported "tracking 35 samples, not enough to fit
    yet" against a threshold of five. The count was never the problem: the
    transport had reached its drop point and begun circling, so the first and last
    readings in the window sat almost on top of each other and the baseline
    collapsed. Refusing to fit was right; explaining it as a shortage of data was
    not, and the two look nothing alike to a player.
    Fit now returns why, and the readout distinguishes "still gathering" from "it
    has stopped going anywhere".
    The everFit guard that should have suppressed the line was set only in the
    branch for a track that has a fit and has not committed. A track that commits
    before that branch ever runs -- which this one did, at five samples -- never
    set it. Set now whenever a fit exists at all.
  • Add the window, a minimap button and a settings panel
    What to show is decided in UI/Model.lua, which is pure and covered; the frame
    code receives a list and paints it. A frame cannot be tested and a table can,
    so as little as possible is decided in the parts that cannot be.
    The window leads with the rotation when one is set, because "when do I leave"
    is the question a rotation asks and a bare countdown does not answer, and
    zones off the route follow so nothing known is hidden. A stale row is dimmed
    and carries its missed-cycle count: RCT draws a timer nobody has confirmed for
    hours exactly like a live one, which is how its Eversong countdown keeps
    promising drops that never come. An empty database says what to do rather than
    sitting blank, since that is the normal state for a new install.
    The headline shows the leading candidate greyed even while the call is
    uncertain. A Zul'Aman flight held the correct spot in first place for a full
    minute before the margin cleared, and saying nothing for that minute is worse
    than saying "probably here". The waypoint still waits for confidence.
    The minimap button is hand-rolled rather than pulled in with LibDBIcon, which
    would mean bundling LibDataBroker and LibStub too -- three libraries for one
    button. Settings uses Blizzard's own API: RCT carries AceConfig and AceGUI,
    including fifteen widget files, to draw checkboxes the game has drawn natively
    since 10.0. Both register on PLAYER_LOGIN in their own frames, so a future API
    change takes out a panel rather than the tracker.
    Also dropped minimapAngle and window from DEFAULTS. They hold where the player
    dragged something, so unset is their real state and every reader has its own
    fallback -- a default of nothing is not a default. The defaults spec caught
    that, correctly.
  • Stop a bad pairing inventing an interval no zone has
    /ewc offsets reported Zul'Aman running an 825-second cycle. It came from a
    timer seeded off a crate found already lying on the ground: that timestamp is
    when the crate was SEEN, any point after it landed, so the gap to the next drop
    came to about 1650 seconds. The cycle arithmetic read 1.5 as 2 and halved it.
    825 then went into GetZoneInterval and out into the countdown, so the measured
    figure was worse than the shipped one it replaced -- the exact failure the
    measurement was meant to avoid.
    Two holes, both here. A gap is now only reported when both ends are
    spawn-anchored, and a gap must sit within fifteen percent of a whole number of
    cycles to count as one. Real readings land within four percent, so that is
    three times the worst observed and still rejects anything near the midpoint.
    Neither existing test would have caught either, which is its own finding. Both
    are covered now and both were checked against the broken code first.
    /ewc interval reset clears measurements, since any database that has been
    running already holds the bad Zul'Aman reading.
  • Add /ewc offsets to test whether zone cycles sit at a fixed phase
    Raids rotate between three and five zones and that only works because the
    drops do not coincide, so the zones are offset. Whether that offset is
    CONSTANT is not something a player can see by eye, and it matters a great deal:
    if it is, catching one drop tells you when every other zone is due, and the
    cold-start problem -- roughly an hour of waiting to learn six zones solo --
    disappears without any player-to-player sync at all.
    This prints each known timer's phase within its cycle and the gap between every
    pair. Run across sessions, a repeating gap for the same pair means the offset
    is fixed; a wandering one kills the idea for the price of looking.
    Recorded honestly alongside it: a shard number is per-zone, so equal numbers in
    two zones mean nothing, which is a real obstacle to reading this and not one I
    can see past yet.
  • Admit the computed release estimate is the weak leg
    A fifth flight ran 54 seconds late against a 27-second estimate. The errors now
    read +16, +22, +29, +31, +54 -- late every time, but across a 38-second spread,
    which a mean cannot represent.
    The cause is that the computation assumes the transport flies straight to its
    drop point at the speed it has been holding, and it does neither reliably. The
    case made here for computing rather than measuring was that measuring needs a
    warm-up, and in exchange it bought an estimate that can be out by a factor of
    three. RCT measures this leg vignette to vignette and by construction has
    neither the bias nor the spread. That was the wrong trade and the comments now
    say so; replacing it is the next piece of work on this file.
    The correction stays, since late-but-corrected beats late, but it needs five
    observations before it applies and reports its spread so the countdown is not
    read as precise.
    Also fixed a line that printed the measured interval labelled as the shipped
    one, which is how Harandar came to report "shipped: 1092s" when the table says
    1100.
  • Record why the descent is not inferred, and split the interval readout
    Watched side by side, RCT's landing countdown reached "on the ground" with
    about thirty seconds of falling left. Its code explains it: rather than use the
    crate's on-ground vignette it decides a crate has landed when the parachute has
    not been seen for sixty seconds, dating the landing to the last sighting plus
    fifteen. That fires whenever a player simply moves out of range of a crate
    still in the air, so every such flight teaches its model a descent shorter than
    the real one -- short, which is the direction observed.
    We measure from the on-ground vignette instead, which one check against a
    player watching a crate land agreed with to a second. Written down so it does
    not get "improved" into an inference later without evidence that readings are
    actually being lost.
    Single-cycle interval readings have come in consistently below multi-cycle
    ones: 1054 and 1060 against 1086 and 1097, with no overlap. Probably because
    arriving mid-flight dates the first drop late while the second, waited for, is
    caught promptly. /ewc interval now splits the two so the pattern stays visible;
    if it holds, single-cycle readings should be excluded rather than weighted down.
  • Trust the measured interval over the shipped one
    Harandar has now been timed twice, once across four drops and once across one,
    and they agree at 1086 and 1085. Voidstorm came out at 1097 across four. So the
    1100 every addon ships is not right, and the eleven seconds between those two
    zones says it may not even be one number. Assuming 1100 where the truth is 1086
    puts a countdown almost a minute out over four cycles, which for a raid
    deciding when to leave is the whole point of the timer.
    GetZoneInterval prefers what this client has observed once a zone has three
    cycles of evidence behind it. One cycle is not enough -- it carries the whole
    detection error at both ends, which is what made the lone Slayer's Rise reading
    of 1054 an outlier against everything measured since.
    Hardcoding the new figures would be the same mistake with better inputs, so
    they stay in the comment as the reason and out of the table.
    I was wrong twice on the way here and both are worth recording: "1100 is wrong"
    off a single one-cycle reading was premature, and the retraction that followed
    was over-cautious. The four-cycle readings settled it.
  • Correct the release estimate by its own measured bias
    Two flights promised release in 8 seconds and took 30 and 37 -- late by 22 and
    29, both in the same direction, which is a bias rather than noise and a large
    one against an eight-second estimate. Most likely the transport slows on
    approach while the heading fit reports an average over its whole window,
    though the cause does not matter for correcting it.
    The correction is the mean of what has actually been observed, pooled across
    zones because the cause is not zone-specific and the samples are few. It only
    applies from two observations on, so one odd flight cannot swing it.
    The raw estimate is what gets remembered and scored against. Scoring the
    corrected figure would drive the measured bias towards zero and quietly remove
    the correction that earned it -- a self-cancelling feedback loop that would
    look like the model getting better.
  • Flag a descent measured while the parachute was still drawn
    The game keeps drawing a parachute for a crate that is already down. Observed
    live in Zul'Aman, lingering eight to ten seconds, which is very likely the
    whole difference between the 98s measured there and the 87s measured cleanly
    in Harandar. Two zones that looked like they behaved differently may simply be
    the same descent measured twice, once through a lagging vignette.
    The scan now notes which zone and shard show a parachute before acting on
    anything, and a landing recorded while one is still up carries the flag. It is
    not rejected: whether those readings really run long is something a handful of
    samples will show and a guess will not, and discarding every reading from a
    lifecycle that misbehaves regularly would leave almost no data.
    Descents are kept as individual readings rather than a running sum, as the
    interval gaps already are. With one reading per zone a mean is not a
    measurement, and /ewc airtime now lists them so the spread and the flags are
    visible rather than averaged away.
  • Weight the interval by cycles, and number GUID fields correctly
    Two interval measurements now exist and they disagree: a one-cycle Slayer's
    Rise gap of 1054s and a four-cycle Harandar gap averaging 1086s. The longer one
    deserves more weight, because detection error at each end is divided by the
    number of cycles it spans -- four drops is a quarter of the error and therefore
    four times the evidence. The mean is total elapsed over total cycles rather
    than the average of the per-cycle figures, which would have treated them as two
    equal votes and landed between them for no reason.
    The GUID field dump numbered fields 1, 3, 5, 7 because "[^-]*" matches the
    empty run between every pair of separators as well as the fields themselves.
    Shard.FromGUID was reading the right positions all along -- shard 39, instance
    2694 are genuinely fields 5 and 4 -- so only the diagnostic lied, which is
    still worth fixing since the point of a diagnostic is to be believed.
    Settled while here: the transport has a creature GUID but UnitPosition returns
    nothing for it, checked three times. There is no altitude to read, so the
    descent has to be measured rather than computed. That question is closed.
  • Pair the descent by shard, and learn from every landing
    Two bugs from one log, and the first is the worse kind because it produced a
    plausible number. Two crates landed in Slayer's Rise ninety seconds apart on
    different shards -- a Horde raid's on one, an Alliance one on the other, after
    the player was re-sharded out of a group. The descent was timed from one
    crate's parachute to the other's landing and came out as 154 seconds. The true
    figure was 117. It is keyed by zone and shard now, with a cap on how old a
    parachute may be to belong to a landing.
    The second: Learn.Note only ran when the timer moved, so a crate seen falling
    and then on the ground contributed nothing to the catalogue -- the timer
    correctly ignored the second sighting as the same crate, and the landing
    position went with it. That position is the best evidence there is of where
    crates land, and it was being discarded in the common case. Updating a
    countdown and learning a drop point are separate jobs and are no longer tied.
    Also fixed a false positive in tests/lint.py, which read %s inside a format
    string as a call to a function named s. It reported one against Main.lua. A
    linter that cries wolf gets switched off, and is then worth nothing.
  • Measure the interval, and score the release-time estimate too
    First measurement, in Slayer's Rise: 1054 seconds between drops on one shard.
    Every shipped figure misses it -- CrateTrackerZK 1100, RCT 1098-1099,
    WarCrateTracker 1095 -- and HGLog would have thrown it away, since it accepts
    an observation only between 1090 and 1105. This is exactly why the bounds here
    are wide: a model that only accepts what it expects cannot find out that the
    expectation is wrong. One observation is not proof of 1054, but it is enough to
    show 1100 is not right.
    The landing countdown on the same flight promised release in 8 seconds and it
    took 30. The transport may slow on approach while the heading fit reports an
    average over its whole window, but that is a guess, so the addon now scores its
    own release estimate against the actual release and keeps the running bias --
    the same treatment the position estimate already gets, which is the only reason
    anyone knows it lands within a third of a percent.
  • Report world height in /ewc shard, to settle whether descent is computable
    Vignettes carry x and y only, and the API has no way to ask what the ground is
    doing at an arbitrary point -- no raycast, no GetGroundHeight, deliberately.
    None of the three competing addons reads a height at all.
    UnitPosition is the one call that reports a z, and only for something with a
    unit id. The transport's atlas is Vehicle-Air-Occupied, so it may well be a
    targetable vehicle rather than a bare map marker. If it is, its altitude is
    readable, the ground under a drop point is readable by standing on it, and the
    descent becomes arithmetic instead of something to measure.
    That reduces to one question answerable by mousing over the plane, so this
    prints the answer rather than arguing about it.
  • Add the landing countdown, computing the leg that can be computed
    RCT shows two legs -- transport to drop point, then parachute to ground -- and
    learns both, per zone and per drop point, with a running mean it will not trust
    until four crates have been timed at that exact spot. Until then it says
    "Learning".
    Only one of those legs needs learning. The heading fit already reports the
    transport's speed and the prediction already knows which spot it is flying at,
    so the time to release is a division. It works on the first flight, in a zone
    nobody has ever visited.
    The descent cannot be computed -- nothing observable says how high the crate
    was released -- so it is measured, as the gap between the parachute appearing
    and the crate being on the ground. Per zone rather than per drop point: RCT
    splits it by point, which collects data an order of magnitude more slowly, and
    whether the spread within a zone justifies that is a question the samples can
    answer later.
    An unmeasured zone still shows a figure rather than "Learning", flagged as
    resting on a guess. Bounds are 5 to 240 seconds, wide enough to admit the 90 to
    120 observed live.
  • Track one transport per zone, not one per vignette GUID
    The readout went back to alternating COMMIT with "4 samples, not enough to fit
    yet" on a Slayer's Rise flight, which the everFit guard was supposed to have
    settled. It had not, because those were two different tracks: the transport's
    vignette GUID churns, disappearing and returning under a new id every few
    seconds, and keying on it built a fresh track each time with its own sample
    count and its own idea of whether it had committed. One would commit while a
    sibling sat at four samples.
    The GUID cooldown, the everFit guard and the empty-track sweep were each
    treating a symptom of this. A zone has one crate per cycle and therefore one
    transport, so the zone is the honest key; the current GUID is carried on the
    track and replaced as it moves. A GUID going quiet no longer ends anything,
    since the event hands the track its replacement.
    Prediction reads the zone's track directly rather than scanning for it, and
    reports the committed call.
  • A called transport keeps its answer instead of re-guessing past it
    In Coiled Isle the tracker called 46.7, 73.8, held it for a minute at zero
    heading error, and then -- the moment the plane flew through the point -- began
    talking about 57.6, 77.5. A transport carries one crate and drops it once, so
    re-reading its heading afterwards is reading a plane that has finished its job.
    Once called, the call stands. The only question left is whether it got there,
    and saying so earns its place: on that flight the crate's vignette never
    appeared at all, so telling the player the transport reached the point it was
    called for is the only word they get about where the crate went.
    Dropped the announced table with it. It existed to stop a steady prediction
    repeating, which the commitment now does, leaving it written to nil in four
    places and read in none.
  • Line up the route columns and prune dead shard timers
    The countdown was unpadded, so 1:55 and 14:55 shunted everything after them
    sideways and the leave column never lined up.
    The shard re-rolls every time you leave a zone and fly back, so entries pile up
    for shards nobody will stand in again -- three Zul'Aman shards inside one
    evening's farming. They still predict their own shard correctly; the problem is
    that returning to it is chance, so what they contribute is rows to read past.
    Anything more than six cycles stale, nearly two hours, is dropped on login.
    Also removed a defensive expression in the login path that guarded against
    GetZoneInterval being absent. Data/Zones.lua loads before Core/Main.lua, so it
    cannot be, and the guard was the kind of noise this addon exists to avoid.
  • HD for Harandar, and let Describe be the canonical spelling
    Parsing stays forgiving -- hd, HD, harandar all resolve -- while Describe
    writes the one form the addon uses. The round-trip test asserted the input
    spelling came back unchanged, which only held while the two happened to match.
  • Ship travel estimates and the route commands
    Silvermoon has portals to Harandar, Voidstorm and Coiled Isle; Eversong and
    Zul'Aman go by mount, and Eversong is the short one because Silvermoon sits
    inside it. Nothing observed exceeds two minutes.
    The estimates are coarse on purpose. The spread WITHIN a zone -- which of its
    drop points the crate picks, relative to where the portal leaves you -- is
    about as wide as the spread between zones, so a precise per-zone figure would
    be false precision. /ewc travel overrides any of them.
    Erring low is deliberate and the asymmetry is real: overstating travel makes
    the planner call a reachable drop 'missed' and the player skips a crate they
    would have caught, while understating it sends them on a flight they were
    going to make anyway and the grace period absorbs arriving a little late.
    Also note that travel only decides anything in the last two minutes before a
    drop. On an eighteen-minute cycle the rest of the time the answer is obvious.
  • Measure the respawn interval instead of assuming it
    Three addons ship three different figures -- CrateTrackerZK 1100, RCT 1098-1099,
    WarCrateTracker 1095 -- and none of them measured. HGLog does accumulate
    observations, but only accepts one between 1090 and 1105, so its learning can
    confirm the number it was given and can never discover a different one. That is
    an assumption wearing a measurement's clothes.
    The gap between two drops in one zone on one shard is the only direct
    observation of the interval anybody gets. Timers.Record now returns it, and
    NoteGap files it with bounds wide enough (240-7200s) not to prejudge the
    answer. A gap spanning drops that were missed is divided down rather than
    discarded.
    Raw observations are kept rather than folded into a running mean: the question
    is what the interval is, and a mean cannot show whether the values cluster
    tightly or scatter. /ewc interval prints them.
  • Stop tracking a transport in a zone whose crate has already landed
    The transport does not despawn when it drops its cargo, it circles, so it goes
    on reporting itself as flying with nothing left to predict. Ending its track on
    the drop was not enough: its vignette GUID churns, so the very next scan built
    a brand new track with fresh state, which then narrated its own sample count
    from scratch -- 2, 3, 2, 3 for as long as the plane stayed in the air.
    A zone is left alone for two minutes after a crate comes down there. The next
    one is around eighteen minutes out, so the silence costs nothing.
    Also worth recording from the same flight: heading error sits at 0.00 degrees
    on a straight run and climbs past 2.5 to 4 as the transport turns. No separate
    turning threshold is needed after all -- the acceptance cone already carries
    2.5 sigma of that error, so a circling plane widens it until nothing can meet
    the margin and it simply stops committing. That was the behaviour I wanted to
    see measured before trusting, and it holds.
  • Add the rotation planner: when to leave, not just what is soonest
    Corrected model, from how raids actually run this. A crate gets taken, the raid
    takes a mage portal to the capital, and flies out from there to the next zone
    on a route agreed beforehand -- three to five zones, cycled. Nobody flies zone
    to zone, so travel is one capital-to-zone number per zone rather than a matrix
    over every pair, and the earlier geometry probe was measuring the wrong thing.
    What a sorted timer list gets wrong follows directly: a drop 40 seconds out in
    a zone four minutes away is not 'next' in any useful sense. It is the drop you
    are guaranteed to miss, sitting at the top of the list above the one you could
    have caught. Route.Plan marks each as go, wait, missed or unknown, and Next
    returns the soonest one still reachable.
    Carries a grace period: arriving a few seconds after a crate lands is ordinary
    farming, not a miss.
    Travel times are a placeholder 60s for now and are meant to be measured, not
    guessed at -- the number is deliberately one value so it is obvious it has not
    been earned yet.
  • Move a comment back onto the function it describes
  • Add zone abbreviations and a geometry probe for routing
    Abbreviations are what farmers actually say and what a route editor has to
    accept as input. Taken from WarCrateTracker (MIT, Samuel Colburn), which
    carries the same set. Parsing is deliberately forgiving -- ES and EW both mean
    Eversong, since this comes out of a text box.
    /ewc geo asks C_Map where each of the six zones sits on its parent map and
    prints the distances between them. Routing needs to know how long a hop takes,
    and the first thing to establish is whether the zones share a coordinate space
    at all rather than assuming they do.
  • Stop the readout alternating COMMIT with 'not enough samples'
    Samples age out of the window and arrive again, so a tracked transport's count
    oscillates across the fit threshold for its whole flight. The previous guard
    only suppressed a falling count, which does not cover 5, 4, 5, 4 -- half a
    92-second flight's log was that alternation.
    The waiting line now belongs strictly to acquiring a track: said once per count
    on the way up, and never again once the track has fitted.
  • Quieten a dying track, and bury it when it is empty
    Flying out of a transport's range now counts the samples back down as they age
    out of the window, which is the trim working, but narrating '4, 3, 2, 1' on the
    way to a death already decided is noise. The waiting line is reported only
    while the count is climbing.
    A track with no samples left is dropped at once rather than sitting out
    TRACK_STALE so it can be narrated at.
    Written without goto deliberately: the syntax pass runs Lua 5.5 via lupa and
    WoW runs a 5.1-based dialect, so a 5.2+ construct parses green here and proves
    nothing about the client. Said so in the runner next to the check.
  • Correct the sample-rate note: the cadence varies, it is not a fixed 5.5s
    One Voidstorm flight yielded a position about every 5.5 seconds; a Zul'Aman
    one reached thirty samples in eighteen. The event feeds the track alongside
    the poll and fires far more often for some flights than others, so the poll is
    a floor under the rate rather than what sets it. The old note stated the slow
    case as if it were the API's fixed behaviour, which is exactly the kind of
    measurement someone would later tune MIN_SAMPLES against.
  • Stop discarding the target the moment the transport reaches it
    A flight in Zul'Aman predicted 48.9, 69.2 and the crate landed at 46.9, 62.2 --
    7.2% of the map out. The log shows the addon had ranked the correct spot first
    for a solid minute beforehand, then changed its mind at the last moment and
    committed to the wrong one.
    MIN_AHEAD dropped any spot nearer than 4% of the map from the candidate list,
    on the reasoning that there is nothing to predict about a spot you are already
    on top of. What it actually did was remove the transport's real target exactly
    as it arrived, leaving the next spot along the same bearing as the unopposed
    front runner. A prediction that gets worse the closer it gets is worse than no
    prediction.
    Candidates are now everything ahead of the transport. Arrival is a separate
    test made on distance rather than bearing -- close to a spot and on the ray
    means that spot, which is also where the angular test stops meaning anything,
    since the cone is SCATTER/along and blows up as along goes to zero.
    Ties in the ranking now go to the nearer spot. Two spots exactly on the
    bearing cannot be told apart by angle and the transport reaches the near one
    first; leaving that to table.sort is not something a waypoint should rest on.
    Also: "transport spotted" is announced per zone with a cooldown. A transport's
    vignette GUID churns and each reappearance built a fresh track, which printed
    it twenty-five times in forty-five seconds for one plane.
  • Fix two faults a live flight exposed, and stop the readout spamming
    The game yields a new vignette position only about every 5.5 seconds, measured
    over a 69-second flight. Everything below follows from that, and none of it
    was visible from outside the game.
    Trim stopped at MIN_SAMPLES so a briefly-quiet transport would keep its
    heading. But a track sits at exactly the minimum almost all the time at that
    sample rate, so nothing was ever dropped: a landed transport went on
    reporting a fit spanning 86 seconds against a 20-second window. It now trims
    unconditionally and Fit returns nil when too little recent data is left,
    which is the honest answer.
    A crate's vignette carries a different GUID from the transport that dropped
    it, and the transport does not despawn -- it circles its drop point. Clearing
    the track by the crate's own GUID therefore never touched it, and it narrated
    "nothing-ahead" forever. Any crate vignette now ends every transport track in
    that zone.
    The watch readout printed once a second, which meant 47 identical COMMIT
    lines for one flight. It reports on change only, and is off by default now
    that the first flights have been watched.
    Poll drops to 1Hz. At 4Hz it was asking four times for each position the game
    bothers to update, and the sample rate was never ours to raise.
  • Use the real map tree in the zone tests
    Read out of the live client with /ewc map instead of guessed at. Two rows
    carry the whole normalisation rule, and neither was obvious from outside the
    game:
    Silvermoon City is mapType 3 (Zone) sitting directly under Eversong Woods,
    also mapType 3 and a tracked crate zone. A walk that does not stop at the
    first Zone it meets reports a sanctuary as Eversong, then reads a shard there.
    The Den, Midnight's housing area, is mapType 5 (Micro) under Harandar. The
    same walk has to keep going for that one, or the addon goes blind wherever
    housing is.
    Satisfying both at once is why the rule is "stop at the first Zone" rather
    than a table of special cases, and it means no alias is needed for housing in
    any of the six zones. Also confirms Quel'Thalas 2537 is mapType 2, a
    continent, so excluding it from the tracked list was right.
  • Initial commit: crate tracking and drop-point prediction for Midnight
    A War Supply Crate tracker built from scratch. The prediction model is the
    part worth explaining.
    The transport enters a zone from varying map edges, not one fixed launch
    point, so matching a flight against a per-zone origin cannot work. Instead
    this casts a ray from the transport's live position along its measured
    heading and takes the nearest catalogued landing spot. That needs no launch
    point and no learned route database.
    Drop locations turn out to be a discrete fixed set: 5-16 spots per zone,
    with repeat drops landing a median 0.12% of the map apart and distinct spots
    sitting over 4% apart. Established from Wowhead's recorded spawns for object
    290129, cross-checked against a competitor's independently farmed list, which
    agrees on 106 of its 117 points. Data/DropPoints.lua ships 220 spots across
    31 zones; Track/Learn.lua adds the ones that data misses, which Voidstorm
    proved it does.
    Heading comes from regressing x and y on time across the whole sample window,
    not from differencing two samples. Accuracy depends on it almost entirely:
    simulated, picking the right spot the moment a transport appears scores 90%
    at half a degree of error and 35% at eight. Predict refuses to commit when
    the best and second candidates are too close, which is roughly one geometry
    in six and cannot be resolved by flying further.
    Verified live in Zul'Aman: predicted 32.8, 77.5 about two minutes ahead, the
    crate landed at 32.7, 77.6.
    Everything down to Track/ runs under a plain Lua interpreter, so tests/
    covers the maths outside the game. 83 checks, plus a syntax pass and a lint
    for locals used before they are declared, which caught a real nil-global call
    in the scanner.

This mod has no additional files