Script: brd_db
// brd_db.nss — "Roll of the Fallen" boss respawn tracker database helpers.
//
// Campaign DB "respawndb" (file database/respawndb.sqlite3).
//
// Tables:
// boss_registry (resref PK, name, tag, area_resref, area_name, cr,
// respawn_seconds, spawn_type) — the curated list of tracked
// bosses, reseeded from this file on every module load.
// spawn_type 'placed' = se_respawn_inc DelayCommand, exact
// respawn respawn_seconds after death;
// spawn_type 'encounter' = native encounter ResetTime, the boss
// becomes ELIGIBLE after respawn_seconds
// and returns when its lair encounter
// next triggers.
// boss_alias (resref PK, canonical) — variant blueprints that all represent
// the same boss (e.g. the five leveled Xanith The Mad blueprints).
// boss_deaths (resref PK, died_at, killed_by) — one row per currently-dead
// boss; wiped on module load because a restart revives everything.
//
// Membership rule: only bosses that can ever have ONE live instance at a time
// (exactly one placement, or one Max=1 encounter slot). Enforced by
// tests/check_boss_registry.py in the build smoke-test.
//
// Browsed 9-per-page in brd_sign.dlg (the Well of Eru board). Custom tokens:
// 6300-6308 = list row labels 6309 = header / page line
// 6310-6313 = drill-down detail (name, lair, return line, slain-by)
// Per-PC locals:
// int "brd_page_off" row offset (multiples of 9)
// int "brd_total" total dead-boss rows (for [Next page] visibility)
// string "brd_slot_N" registry resref shown in list slot N ("" = hidden)
const string BRD_DB = "respawndb";
// ------------------------------------------------------------
// Schema + registry seed
void BRD_SeedBoss(string sResref, string sName, string sTag, string sAreaRes,
string sAreaName, float fCR, int nRespawn, string sType)
{
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"REPLACE INTO boss_registry(resref,name,tag,area_resref,area_name,cr,respawn_seconds,spawn_type)" +
" VALUES(@r,@n,@t,@ar,@an,@c,@s,@ty)");
SqlBindString(q, "@r", sResref);
SqlBindString(q, "@n", sName);
SqlBindString(q, "@t", sTag);
SqlBindString(q, "@ar", sAreaRes);
SqlBindString(q, "@an", sAreaName);
SqlBindFloat (q, "@c", fCR);
SqlBindInt (q, "@s", nRespawn);
SqlBindString(q, "@ty", sType);
SqlStep(q);
}
void BRD_SeedAlias(string sResref, string sCanonical)
{
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"REPLACE INTO boss_alias(resref,canonical) VALUES(@r,@c)");
SqlBindString(q, "@r", sResref);
SqlBindString(q, "@c", sCanonical);
SqlStep(q);
}
void BRD_InitDb()
{
sqlquery q;
q = SqlPrepareQueryCampaign(BRD_DB,
"CREATE TABLE IF NOT EXISTS boss_registry (" +
"resref TEXT PRIMARY KEY," +
"name TEXT NOT NULL," +
"tag TEXT NOT NULL," +
"area_resref TEXT NOT NULL," +
"area_name TEXT NOT NULL," +
"cr REAL NOT NULL DEFAULT 0," +
"respawn_seconds INTEGER NOT NULL DEFAULT 900," +
"spawn_type TEXT NOT NULL DEFAULT 'placed')");
SqlStep(q);
q = SqlPrepareQueryCampaign(BRD_DB,
"CREATE TABLE IF NOT EXISTS boss_alias (" +
"resref TEXT PRIMARY KEY, canonical TEXT NOT NULL)");
SqlStep(q);
q = SqlPrepareQueryCampaign(BRD_DB,
"CREATE TABLE IF NOT EXISTS boss_deaths (" +
"resref TEXT PRIMARY KEY," +
"died_at TEXT NOT NULL," +
"killed_by TEXT)");
SqlStep(q);
// A restart revives every boss (DelayCommand timers die; areas reload from
// the .git), so pending death rows are stale by definition.
q = SqlPrepareQueryCampaign(BRD_DB, "DELETE FROM boss_deaths");
SqlStep(q);
// Reseed the registry so this file is the single source of truth.
q = SqlPrepareQueryCampaign(BRD_DB, "DELETE FROM boss_registry");
SqlStep(q);
q = SqlPrepareQueryCampaign(BRD_DB, "DELETE FROM boss_alias");
SqlStep(q);
// BEGIN GENERATED REGISTRY — produced by bin/gen-boss-registry.py
// Do not hand-edit; run the generator to refresh (see CLAUDE-boss-tracker.md).
BRD_SeedBoss("creature016", "Theoden's Chosen", "TheodensChosen", "area013", "Helm's Deep: Cave", 9540.0, 900, "placed");
BRD_SeedBoss("summanus", "Summanus", "Summanus", "falseheaven", "Numenor: Noirinan", 2938.0, 480, "encounter");
BRD_SeedBoss("gandalf001", "Gandalf the Gray", "Gandalf", "shirehobbiton001", "Hobbiton", 1914.0, 900, "placed");
BRD_SeedBoss("highwizardofgo_2", "High Wizard of Gondor", "HighWizardofGondor", "towerofthehighwi", "Tower of the High Wizard: The High Wizard's Chamber", 1795.0, 900, "placed");
BRD_SeedBoss("creature019", "Sauron The Abhorred", "Constantinethesecond", "area016", "Mount Doom: Halls of Sauron", 1768.0, 900, "placed");
BRD_SeedBoss("gwathdorlord001", "Gwathdor Lord of the Dark Arts", "GwathdorLord", "gwaththrone", "Gwathdor: Throne of the Lord", 1615.0, 900, "placed");
BRD_SeedBoss("creature009", "Boromir, Steward of Gondor", "BoromirStewardofGondor", "minastirith", "Minas Tirith: City", 1461.0, 900, "placed");
BRD_SeedBoss("creature003", "Denethor, Steward of Gondor", "DenethorStewardofGondor", "area005", "Minas Tirith: Keep", 1459.0, 900, "placed");
BRD_SeedBoss("legolasgreenl001", "Legolas Greenleaf - Silvan Prince of Mirkwood", "LegolasGreenleaf", "area023", "Mirkwoods - Halls of Greenleafs", 1418.0, 900, "encounter");
BRD_SeedBoss("witchking", "Witch King Leader of The Nine", "WitchKing", "thewitchkingsthr", "The Witch King's Throne", 1198.0, 900, "placed");
BRD_SeedBoss("radagastthebro_2", "Radagast the Brown", "RadagasttheBrown", "rhosgobeltower", "Rhosgobel: Tower", 1086.0, 900, "placed");
BRD_SeedBoss("thecursedone002", "Heir of Haldor", "TheCursedOne", "forgottenking", "Dimholt: The Forgotten King", 1066.0, 900, "placed");
BRD_SeedBoss("themightysmag002", "The Mighty Smaug", "TheMightySmaug", "darkcavern", "The Ruins of Dale: Cavern", 1048.0, 900, "placed");
BRD_SeedBoss("gollum", "Gollum", "Gollum", "area007", "Gollum's Chambers", 1029.0, 900, "placed");
BRD_SeedBoss("templemistress_2", "Temple Mistress", "TempleMistress", "blackgatge", "Morannon - The Black Gate", 980.0, 900, "placed");
BRD_SeedBoss("creature007", "Khamul The Ringwraith (Dol Guldur)", "Khamul", "area031", "Dol Guldur: Tower Of Black Magic Nazgul Quaters", 965.0, 900, "encounter");
BRD_SeedBoss("creature007_2", "Khamul The Ringwraith (Carn Dum)", "Khamul", "carndumthrone", "Carn Dum: Throne", 839.0, 900, "placed");
BRD_SeedBoss("elrond001_2", "Elrond", "Elrond", "rivendellupperha", "Rivendell Upper Halls", 829.0, 900, "placed");
BRD_SeedBoss("harethofthehalad", "Hareth of the Haladin", "HarethoftheHaladin", "edoras", "Edoras", 811.0, 900, "placed");
BRD_SeedBoss("creature023_2", "The Rancid Skinner Green Mortal of Khamul", "Skinner", "dolguldurcast001", "Dol Guldur: Tower Of Black Magic Depths", 810.0, 900, "placed");
BRD_SeedBoss("adwarfguardia001", "Gloin King Under the Mountain", "GloinKingUndertheMountain", "lonelymountainma", "Lonely Mountain: Main Hall", 799.0, 900, "placed");
BRD_SeedBoss("creature005", "Arwen, The Evenstar", "ArwenTheEvenstar", "rivendellupperha", "Rivendell Upper Halls", 767.0, 900, "placed");
BRD_SeedBoss("aragornsonofarat", "Aragorn Son of Arathorn", "AragornSonofArathorn", "minastirithgates", "Minas Tirith: Gates", 712.0, 900, "placed");
BRD_SeedBoss("eowyntheshieldma", "Eowyn the Shieldmaiden", "EowyntheShieldmaiden", "area008", "Helm's Deep: Keep", 693.0, 900, "placed");
BRD_SeedBoss("shadowlordof_2", "Shadow Lord of Dol Guldur", "ShadowLordof", "dolguldur", "Mirkwood: Dol Guldur", 660.0, 900, "placed");
BRD_SeedBoss("angmar", "Angmar", "ms_Angmar", "area009", "Angmar's Lair", 637.0, 900, "placed");
BRD_SeedBoss("sarumanthewhi001", "Saruman the White", "ms_orth17", "towerofothancsar", "Tower of Orthanc: Saruman's Chambers", 627.0, 900, "placed");
BRD_SeedBoss("thehighmageofbar", "Gothmog Lord of Barad-Dur", "LordofBaradDur", "baraddurkeep", "Barad-Dur: Keep", 604.0, 900, "placed");
BRD_SeedBoss("grimbeorntheo001", "Grimbeorn the Old Lord of the Beornings", "GrimbeorntheOld", "beorn", "Beorn", 587.0, 900, "placed");
BRD_SeedBoss("basilisk008", "Basilisk King", "BasiliskKing", "area003", "Mirkwood: Crypt Chamber", 564.0, 3000, "encounter");
BRD_SeedBoss("azagoth001", "The Balrog of Moria", "TheBalrogofMoria", "thebalrogofmoria", "Moria: The Balrog of Moria", 555.0, 900, "encounter");
BRD_SeedBoss("highpriestofmora", "High Priest of Morannon", "HighPriestofMorannon", "thetempleofmoran", "The Temple of Morannon", 495.0, 900, "placed");
BRD_SeedBoss("thranduil_2", "Thranduil, King of Eryn Lasgalen", "ThranduilKingofErynLasgalen", "thranduilshall", "Mirkwood: Thranduil's Hall", 476.0, 900, "placed");
BRD_SeedBoss("shadowlordof001", "Shadow Priestess of Dol Guldur", "ShadowLordof", "cavesofdolguldur", "Caves of Dol Guldur", 463.0, 900, "placed");
BRD_SeedBoss("creature002", "Faramir, Prince of Ithillien", "Faramir", "citidelofstars", "Osgiliath: Citidal of the Stars", 451.0, 900, "placed");
BRD_SeedBoss("shopkeep002_2", "High Priest of Gondor", "NW_PriestofGondor", "minastirithtemp", "Minas Tirith: Temple", 447.0, 900, "placed");
BRD_SeedBoss("creature017_2", "Eothain Captian of Helm's Deep", "othain", "helmsdeep001", "Helm's Deep", 440.0, 900, "placed");
BRD_SeedBoss("maggotfarmer", "Maggot Farmer", "MaggotFarmer", "bywater", "Bywater", 436.0, 900, "placed");
BRD_SeedBoss("akhorahil006_2", "Morgrond Captian of Dol Guldur", "MorgrondCaptian", "dolguldurcity", "Dol Guldur City", 433.0, 900, "placed");
BRD_SeedBoss("gimli_2", "Gimli", "Gimli", "halloftruth", "The Halls of Truth", 418.0, 900, "placed");
BRD_SeedBoss("kerufall003", "Kerufall", "ms_keru1", "kerufallslair", "Kerufall's Lair", 418.0, 900, "encounter");
BRD_SeedBoss("urukai010", "The White Hand Destroyer", "ms_iseno10", "isengardouterrim", "Isengard (Outerrim)", 374.0, 900, "placed");
BRD_SeedBoss("gondorianwiza001", "Sarah the Librarian", "GreaterGondorianWizard", "towerofthehig001", "Tower of the High Wizard: The Library", 372.0, 900, "placed");
BRD_SeedBoss("gondorianguar005", "Wart Gondorian Gate Captain", "ms_minas22122", "minastirithgates", "Minas Tirith: Gates", 341.0, 900, "placed");
BRD_SeedBoss("theoden002", "Theoden King of Rohan", "ms_edort5", "theodenshall", "Theoden's Hall", 338.0, 900, "placed");
BRD_SeedBoss("bauglirthegene_2", "Deekin the General", "DeekintheGeneral", "orcarmory", "Orc Armory", 336.0, 900, "placed");
BRD_SeedBoss("badass_2", "Fell Beast", "balsum", "kallristcryptlow", "Kallrist Crypt", 333.0, 900, "placed");
BRD_SeedBoss("glorfindel002", "Glorfindel", "Glorfindel", "thevalleyofriven", "The Valley of Rivendell", 323.0, 900, "placed");
BRD_SeedBoss("creature013", "Sarumon's Master Experiment", "SarumonsMasterExperiment", "towerofothancsar", "Tower of Orthanc: Saruman's Chambers", 316.0, 900, "placed");
BRD_SeedBoss("strongminitao002", "Gwathdor Minitaour", "StrongMinitaour", "gwathtomb", "Gwathdor: Tomb", 280.0, 900, "placed");
BRD_SeedBoss("akhorahil004", "Dol Guldur Orc General", "DolGuldurOrcGeneral", "dolguldurcity", "Dol Guldur City", 277.0, 900, "placed");
BRD_SeedBoss("creature012_2", "Thragg, Gate Commander", "ThraggGateCommander", "blackgatge", "Morannon - The Black Gate", 267.0, 900, "placed");
BRD_SeedBoss("rangerofthe002", "Tren Notteel", "Rangerofthe", "rivendell", "Rivendell", 267.0, 900, "placed");
BRD_SeedBoss("frost001", "Frost", "Frost", "emynarnen", "Emyn Arnen: Peak", 256.0, 3000, "encounter");
BRD_SeedBoss("greaterelvenw003", "Celeborn", "ms_lorien21", "lothlorienflets", "Lothlorien: Flets", 250.0, 900, "placed");
BRD_SeedBoss("anaraliathewidow", "Anaralia, The widow maker", "AnaraliaThewidowmaker", "mirkwoodcentrale", "Mirkwood: Central East", 248.0, 900, "placed");
BRD_SeedBoss("kainnatheward001", "Kainna the War Dancer", "ms_isen24", "isengardinnerrim", "Isengard (Innerrim)", 239.0, 900, "placed");
BRD_SeedBoss("jester", "Jester", "Jester", "area008", "Helm's Deep: Keep", 236.0, 900, "placed");
BRD_SeedBoss("creature020", "Mashano, Messenger To The Witch King", "MashanoMessengerToTheWitchKing", "nazgulguildhouse", "House of Nazgul", 236.0, 900, "placed");
BRD_SeedBoss("shelob001", "Shelob", "ms_shel1", "shelobslair", "Shelob's Lair", 220.0, 900, "encounter");
BRD_SeedBoss("adwarfajudica003", "Fifur the Craftsman", "FifurtheCraftsman", "thehallsoftruth", "Temple of Marr", 212.0, 900, "placed");
BRD_SeedBoss("arohirrimsold006", "Card Mennard", "ms_edor1", "edoras", "Edoras", 201.0, 900, "placed");
BRD_SeedBoss("gntfire003", "Gorg the General", "ms_gund27", "mountgundabadlev", "Mount Gundabad", 191.0, 900, "placed");
BRD_SeedBoss("garbag", "Garbag Gulon", "Garbag", "orcarmory", "Orc Armory", 179.0, 900, "placed");
BRD_SeedBoss("trollking", "Troll King", "TrollKing", "thetrollshalls", "The Trollshaws", 155.0, 900, "placed");
BRD_SeedBoss("galadriel", "Galadriel", "Galadriel", "lothlorien", "Lothlorien: Main", 150.0, 900, "placed");
BRD_SeedBoss("railthewhiteh001", "Rail the White Hand Blaster", "ms_isen1", "isengardinnerrim", "Isengard (Innerrim)", 146.0, 900, "placed");
BRD_SeedBoss("drgblue004", "The Ice Queen", "TheIceQueen", "icequeennest", "Ice Queen: Nest", 133.0, 900, "placed");
BRD_SeedBoss("themutantterror", "The Mutant Terror", "TheMutantTerror", "balinstomb", "Balin's Tomb", 132.0, 500, "encounter");
BRD_SeedBoss("cultmember002_3", "Numanan Numerocks Second Hand", "ms_carn26", "carndumcity", "Carn Dum: City", 130.0, 900, "placed");
BRD_SeedBoss("cultmember002_2", "Numarok The Black Hand", "ms_carn26", "area078", "Carn Dum: Numerok's Den", 130.0, 900, "placed");
BRD_SeedBoss("urukai013", "Marshall of the White Hand", "ms_orth5", "toweroforthanc", "Tower of Orthanc", 107.0, 900, "placed");
BRD_SeedBoss("drowfight023_2", "Elor Desh", "BlackNumenorianMilitia", "carndum", "Carn Dum", 106.0, 900, "placed");
BRD_SeedBoss("railthewhiteh003", "Fail the White Hand Blaster", "ms_isenm1", "isengardmagicroo", "Isengard (Magic Room)", 96.0, 900, "placed");
BRD_SeedBoss("drowrogue024_2", "Morfiril Cendel", "BlackNumenorianShadowMaster", "carndumtemple", "Carn Dum: Temple", 89.0, 900, "placed");
BRD_SeedBoss("gondorianguar0_4", "Gondorian Bowman", "ms_minas1", "watchmandefender", "Minas Tirith: Watchman Defender's Head Quarters", 85.0, 900, "placed");
BRD_SeedBoss("gondorianguar0_2", "Gondorian Guardsman", "ms_minas2", "watchmandefender", "Minas Tirith: Watchman Defender's Head Quarters", 80.0, 900, "placed");
BRD_SeedBoss("adwarfajudica0_3", "Dellinth the Advocet", "ms_lone37", "halloftruth", "The Halls of Truth", 63.0, 900, "placed");
// END GENERATED REGISTRY
}
// ------------------------------------------------------------
// Recording
string BRD_Canonical(string sResref)
{
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"SELECT canonical FROM boss_alias WHERE resref=@r");
SqlBindString(q, "@r", sResref);
if (SqlStep(q)) return SqlGetString(q, 0);
return sResref;
}
// Walk the master chain to the owning PC; OBJECT_INVALID if none is a PC.
object BRD_OwningPC(object o)
{
while (GetIsObjectValid(GetMaster(o))) o = GetMaster(o);
if (GetIsPC(o) && !GetIsDM(o)) return o;
return OBJECT_INVALID;
}
// Record a tracked boss's death (no-op for anything not in the registry).
// Called from bst_ondeath, so the bestiary damage-contributor list
// (bst_ctrb_N locals, gathered by bst_ondamage) is available on the corpse;
// the killing blow's owning PC is folded in for one-shot kills.
void BRD_RecordDeath(object oCre)
{
string sRef = BRD_Canonical(GetResRef(oCre));
sqlquery qc = SqlPrepareQueryCampaign(BRD_DB,
"SELECT 1 FROM boss_registry WHERE resref=@r");
SqlBindString(qc, "@r", sRef);
if (!SqlStep(qc)) return;
string sKilledBy = "";
object oKiller = BRD_OwningPC(GetLastKiller());
int bKillerListed = FALSE;
int nN = GetLocalInt(oCre, "bst_ctrb_n");
int i;
for (i = 0; i < nN; i++)
{
object m = GetLocalObject(oCre, "bst_ctrb_" + IntToString(i));
if (!GetIsObjectValid(m) || !GetIsPC(m) || GetIsDM(m)) continue;
if (m == oKiller) bKillerListed = TRUE;
if (sKilledBy != "") sKilledBy += ", ";
sKilledBy += GetName(m);
}
if (GetIsObjectValid(oKiller) && !bKillerListed)
{
if (sKilledBy != "") sKilledBy += ", ";
sKilledBy += GetName(oKiller);
}
if (sKilledBy == "") sKilledBy = "unknown forces";
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"REPLACE INTO boss_deaths(resref,died_at,killed_by)" +
" VALUES(@r,datetime('now'),@k)");
SqlBindString(q, "@r", sRef);
SqlBindString(q, "@k", sKilledBy);
SqlStep(q);
}
// ------------------------------------------------------------
// Board conversation (brd_sign.dlg)
// "12m 30s" / "45s"
string BRD_FmtRemain(int nSec)
{
if (nSec < 0) nSec = 0;
int nMin = nSec / 60;
int nS = nSec % 60;
if (nMin > 0) return IntToString(nMin) + "m " + IntToString(nS) + "s";
return IntToString(nS) + "s";
}
// TRUE when a living creature with sTag stands in area sAreaRes.
int BRD_BossAlive(string sTag, string sAreaRes)
{
int n = 0;
object o = GetObjectByTag(sTag, n);
while (GetIsObjectValid(o))
{
if (GetObjectType(o) == OBJECT_TYPE_CREATURE && !GetIsDead(o)
&& GetResRef(GetArea(o)) == sAreaRes)
return TRUE;
n++;
o = GetObjectByTag(sTag, n);
}
return FALSE;
}
// Drop rows whose boss is back: placed bosses respawn exactly on schedule, so
// an expired row is stale; encounter bosses only return when the lair
// re-triggers, so keep their row ("ready to return") until the boss is seen
// alive in its area.
void BRD_CleanupExpired(object oPC)
{
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"SELECT d.resref, r.tag, r.area_resref, r.spawn_type" +
" FROM boss_deaths d JOIN boss_registry r ON r.resref=d.resref" +
" WHERE r.respawn_seconds - (strftime('%s','now') - strftime('%s',d.died_at)) <= 0");
int nDel = 0;
while (SqlStep(q))
{
string sRef = SqlGetString(q, 0);
string sTag = SqlGetString(q, 1);
string sArea = SqlGetString(q, 2);
string sType = SqlGetString(q, 3);
if (sType == "placed" || BRD_BossAlive(sTag, sArea))
{
SetLocalString(oPC, "brd_del_" + IntToString(nDel), sRef);
nDel++;
}
}
int i;
for (i = 0; i < nDel; i++)
{
sqlquery qd = SqlPrepareQueryCampaign(BRD_DB,
"DELETE FROM boss_deaths WHERE resref=@r");
SqlBindString(qd, "@r", GetLocalString(oPC, "brd_del_" + IntToString(i)));
SqlStep(qd);
DeleteLocalString(oPC, "brd_del_" + IntToString(i));
}
}
// Build the current list page: only currently-slain bosses, mightiest (highest
// CR) first, time-to-return at the end of each label.
void BRD_BuildPage(object oPC)
{
BRD_CleanupExpired(oPC);
int nOff = GetLocalInt(oPC, "brd_page_off");
sqlquery qc = SqlPrepareQueryCampaign(BRD_DB,
"SELECT COUNT(*) FROM boss_deaths d" +
" JOIN boss_registry r ON r.resref=d.resref");
int nTotal = 0;
if (SqlStep(qc)) nTotal = SqlGetInt(qc, 0);
SetLocalInt(oPC, "brd_total", nTotal);
int i;
for (i = 0; i < 9; i++)
{
DeleteLocalString(oPC, "brd_slot_" + IntToString(i));
SetCustomToken(6300 + i, "");
}
if (nTotal == 0)
{
SetCustomToken(6309,
"All the great powers of Middle-earth stir once more. None lie slain.");
return;
}
int nPages = (nTotal + 8) / 9;
int nPage = nOff / 9 + 1;
SetCustomToken(6309, IntToString(nTotal)
+ (nTotal == 1 ? " power lies" : " powers lie")
+ " slain, mightiest first. (Page " + IntToString(nPage)
+ " of " + IntToString(nPages) + ")");
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"SELECT d.resref, r.name, r.spawn_type," +
" r.respawn_seconds - (strftime('%s','now') - strftime('%s',d.died_at))" +
" FROM boss_deaths d JOIN boss_registry r ON r.resref=d.resref" +
" ORDER BY r.cr DESC, r.name ASC LIMIT 9 OFFSET @off");
SqlBindInt(q, "@off", nOff);
i = 0;
while (SqlStep(q) && i < 9)
{
string sRef = SqlGetString(q, 0);
string sName = SqlGetString(q, 1);
string sType = SqlGetString(q, 2);
int nRemain = SqlGetInt(q, 3);
SetLocalString(oPC, "brd_slot_" + IntToString(i), sRef);
string sWhen;
if (nRemain > 0) sWhen = "returns in " + BRD_FmtRemain(nRemain);
else sWhen = "ready to return"; // expired encounter boss
SetCustomToken(6300 + i, sName + " - " + sWhen);
i++;
}
}
// Populate the drill-down detail tokens for one slain boss.
void BRD_BuildDetail(object oPC, string sResref)
{
SetCustomToken(6310, "");
SetCustomToken(6311, "");
SetCustomToken(6312, "");
SetCustomToken(6313, "");
sqlquery q = SqlPrepareQueryCampaign(BRD_DB,
"SELECT r.name, r.area_name, r.spawn_type, d.killed_by," +
" r.respawn_seconds - (strftime('%s','now') - strftime('%s',d.died_at))" +
" FROM boss_deaths d JOIN boss_registry r ON r.resref=d.resref" +
" WHERE d.resref=@r");
SqlBindString(q, "@r", sResref);
if (!SqlStep(q))
{
SetCustomToken(6310, "The ink has faded; this power walks again.");
return;
}
string sType = SqlGetString(q, 2);
int nRemain = SqlGetInt(q, 4);
SetCustomToken(6310, SqlGetString(q, 0));
SetCustomToken(6311, SqlGetString(q, 1));
string sWhen;
if (nRemain > 0)
{
sWhen = "Expected to return in " + BRD_FmtRemain(nRemain) + ".";
if (sType == "encounter")
sWhen += " It rises only when its lair is next disturbed.";
}
else
sWhen = "It may return the moment its lair is next disturbed.";
SetCustomToken(6312, sWhen);
string sKilledBy = SqlGetString(q, 3);
if (sKilledBy == "") sKilledBy = "unknown forces";
SetCustomToken(6313, sKilledBy);
}