Skip to content

Quests

A quest is one Lua file: a table with a giver NPC, a text, objectives and rewards. One engine, scripts/common/quest.lua, runs all of them: it offers them, counts the objectives, turns them in and keeps a quest log. It follows the model of the Mondain’s Legacy quests of ModernUO, but a quest is data in Lua, not a class in C#.

  1. The giver. An NPC that has a quest for the player to offer, to follow or to turn in, or that a quest going waits for a talk with, offers the client’s Quest Conversation entry in its context menu. Double-clicking such an NPC does the same, from within 6 tiles and not as a ghost, and opens the quest gump instead of its paperdoll. An NPC with nothing for the player keeps its paperdoll and its menu. The other things the NPC does (a shop, a bank, a healer) are not touched.
  2. The gump of a giver shows, in this order, the quests the player can turn in, the ones going, and the new ones it may offer. With one quest, that one opens at once.
  3. The offer has the title, the text, the objectives and the rewards, with Accept and Refuse. Accepting and turning in need the player to stand within 6 tiles of the giver.
  4. Going. Every kill or talk writes a line with the counter (“Kill ten sewer rats: 3 of 10”); when every objective is full a line says to go back to the giver. An escort sets the NPC that gives the quest walking after the player: it keeps up (running when the player is far ahead), thanks the player when it stands in the destination region, and the quest is turned in there at once. A quest that asks for a delivery puts the item in the pack when it is accepted: the objective is done when the player talks to the NPC it is for, and the quest is turned in there. An objective with a time limit shows the time left; when it runs out the quest fails: the quest log marks it (failed), its delivery is taken back and the giver says nothing of it until the player abandons it and takes it again.
  5. Turning in. The gump of the giver has a Complete button when every objective is full. The collected items are taken, the rewards given, and the next quest of a chain (next) is offered at once when it has the same giver; one with another giver only becomes available there, and a line names it. A quest that is turned in is done for good the moment its items are taken: a reward is never given twice.
  6. The quest log. The Quest button of the paperdoll (packet 0xD7, subcommand 0x32) opens a list of the quests going; a quest of the list shows its objectives with their counters and an Abandon button, which asks again before it drops the quest and its counters. A player with a quest going also finds View Quest Log in its own context menu.

A quest survives the death of the player and a restart of the server: its progress is kept on the character.

scripts/quests/<id>.lua returns a table. The id is lowercase letters, digits and underscores. A quest is on the shard once its id is in scripts/quests/index.lua (Lua cannot list a folder). script reload events/quest.lua reads the index and every quest file again, so an edited quest, or a new one, is picked up without a restart; the progress of the players is not touched.

-- scripts/quests/sewer_rats.lua
return {
title = "#40100",
giver = "healer", -- the template of the NPC that offers it
description = "#40101",
objectives = {
{ kind = "kill", target = { "sewerrat", "giantrat" }, amount = 10, text = "#40102" },
},
rewards = { { gold = 100 }, { item = "clean_bandage", amount = 5 }, { karma = 10 } },
next = "another_quest", -- optional: the quest offered after this one is turned in
repeat_hours = 24, -- optional: may be done again that many hours after it was turned in
}
Field Meaning
title, description The texts of the offer, the progress and the log
giver The mobile template that offers the quest and takes it in, or a list of templates ({ "tiana", "saril" }): any of them does
objectives A list; every objective has to be full, unless the quest has any = true
region, outside Optional, the name of a region: the giver offers the quest only while it stands in region, or not in outside (the escorts of New Haven and of the towns)
any Optional, true: the quest is complete when any one of its objectives is, and the turn-in takes only the items of the first full one. The gumps say Objectives (any one of them)
rewards A list of rewards, optional; reward_text replaces the line that names them in the gumps
refusal, in_progress, completion Optional lines, texts like the title: said when the offer is refused, shown on the progress gump while the quest is going, and said when it is turned in
next The id of the quest that follows. A quest that is some quest’s next can only be offered once that one was turned in
repeat_hours Without it a quest is done once; with it, it comes back that many hours after the last turn-in
kind Fields Counts
kill target (a template id, or a list of them), amount A death of that template by the player, a pet of the player, or a member of its party in sight (18 tiles) that has the quest going and a counter not yet full
collect item (a template id, or a list of them: any of them counts), amount The items of that template the player carries (not what it wears) when it turns the quest in, in any number of stacks; they are taken then
talk npc (a template id) The first time the player opens the quest interaction of that NPC, from within 6 tiles; the NPC needs no script of its own
escort region (the name of a region of data/regions), region_name (optional text: how the gump calls it) The giver walks after the player (a step at every look, five a second, when far behind; a walk when near) and the objective is done when the giver stands in that region (or in one that has it as a parent): it thanks the player (the client’s text 1042809) and the quest is turned in at once. Only one escort at a time and five minutes of rest after setting out; not offered by an NPC that is escorted already, nor at its destination. A player who goes through a gate, a teleporter or a recall takes the NPC that was beside it along. The quest is dropped with a message when the player is more than 30 tiles away for a minute (the NPC says the client’s 500902 and keeps looking for the player meanwhile), when the NPC dies or is gone, and without one when the player dies. A player who logs out leaves the NPC waiting a minute; the quest then fails at the first interaction after the login. A restart ends the escorts going the same way. However an escort ends, the NPC is deleted 100 seconds later (a spawn region makes another), unless another escort has it by then. Another NPC of the giver’s kind, double-clicked while the player has an escort going, says “I see you already have an escort” (the client’s 500896)
deliver item (a template id), amount, npc (the template the delivery is for), npc_name (optional text: how the gump calls it), spawn (optional, false: the item is not made) The player talks to that NPC (from within 6 tiles) with the quest going: the objective is done then, and the quest is turned in at that NPC, not at the giver, as ModernUO moves the quest to it. amount items of the template are made in the pack when the quest is accepted (where the player stands if the pack takes nothing) and taken again at the turn-in (a player who lost them is told what it holds and cannot turn the quest in), or when the quest is abandoned or fails
skill skill (the name of a skill: "Swordsmanship"), amount (whole points) The skill the character has when it is asked, in whole points, never above amount: the objective is full when the skill gets there, as ModernUO’s GainSkillObjective; a giver does not offer the quest to a player who has the skill already

Any objective may have a time limit, seconds = 600: the quest fails when it runs out, or, for a quest that asks for any one of its objectives, when every objective has run out (an objective with no limit never does). The time is checked at every interaction with the quest (a kill, a talk, a turn-in, the log) and by a timer set when the quest is accepted; one that ran out while the player was away is noticed at the next interaction. A failed quest stays in the log until it is abandoned, counts nothing and cannot be turned in.

A kill objective may be limited to a region: area = "Sanctuary" (the name of a region of data/regions, any map) counts only a death whose corpse lies in that region or in a region that has it as a parent (world.in_region); a death with no known place does not count. area_name is the text the gumps use for it (a message such as "#1074807"), else the name of the region: “Kill 3 (rats) in Sanctuary”.

Every objective may have a text (the whole line for the gump) or a noun (the thing it counts, which the engine puts in its own lines: “Kill 10 (rabbits)”, “rabbits: 3 of 10”); without either the gump names the amount and the template. Templates match exactly: an id does not stand for the templates that inherit from it. A death counts for a quest only when the killer is a player or the pet of one: a guard, a spell or a poison with no player behind it counts for nobody.

{ gold = n }, { item = "<template>", amount = n } (an amount above 1 only for an item that stacks), { skill = "<name>", points = n } (raises the skill up to its cap), { fame = n } and { karma = n } (kept within the limits of the client, with no message), and { loot = "<table>", rolls = n, bag = "<template>" }: a bag (the bag template unless another is named) is made in the backpack and the loot table of templates/loots is rolled into it rolls times (1 when left out); it takes one slot of the pack, whatever it holds, and the gumps call it “a bag of rewards”. { flag = "<name>" } grants the player a flag for good (the prop quest.flag.<name>, read with quest.has_flag): it says nothing and takes no slot; nothing reads the flags yet (the ability to summon the fey, to learn Spellweaving), they wait for the features that will. Gold, amounts, fame and karma are whole numbers; a quest with another kind of value is left out.

Before anything moves, the turn-in checks that the backpack has room for all the reward items together (item.has_room_for_all: what joins a stack needs no slot, the rest takes one each); with no room the player is told and nothing is taken or marked. Then the collected items are taken, the quest is marked as done and leaves the log, and only then are the rewards given: into the pack, or where the player stands when the pack cannot take them. A reward that cannot be given at all is said in a line and the quest stays done.

Two more fields of a quest: reward_message, a text (like the title) said to the player when the quest is turned in, after the rewards (the boons of the Heritage quests), and accept_flags, a list of flag names granted when the quest is accepted and kept if it is abandoned (the access to Bedlam).

A text that starts with # is a message number: "#40100" is message 40100 in the language of the server (the English text where the language lacks it). Any other text is shown as written. Messages may carry {0}, {1} values. The messages of the quests are in their own file, data/messages/<language>/quests.toml, one per shipped language, with numbers from 40000: the lines of the engine and its gumps, and the texts of the example quests. See Localization.

Five example quests in New Haven ship in scripts/quests: Rats in the cellar (kill, the healer), Hides for the tanner (collect), a chain of two from the scribe (A word with the innkeeper and A word with the tanner, each a talk objective), and Eggs for the herbalist (collect, repeatable every 24 hours).

  • State is a set of props on the character, kept with it: quest.active (the ids going), quest.<id>.p<i> (the counters), quest.<id>.at and quest.<id>.done (the times), quest.<id>.t<i> (when the time of an objective ends) and quest.<id>.failed.
  • scripts/events/quest.lua starts the engine, counts the deaths of the mobile_died event (the victim’s template is read from its corpse) and answers the calls of the server: quest.on_npc_used(player, npc) for the double-click, quest.is_giver(npc) and quest.has_active(player) for the context menus, quest.on_journal(player) for the Quest button.
  • The gumps are in scripts/common/quest_ui.lua, built with the Lua gump API.
  • A quest file with a mistake (no giver, an unknown kind, an objective with no amount, a reward that is not whole, a next that does not exist or that leads round to the quest, two quests that lead to the same one) is left out and written to the log; the others still load. Whether the templates it names exist is not checked at load: the tests of the shipped quests do it.

ModernUO’s 257 Mondain’s Legacy quests (its list has 265 entries: seven are commented out in its source, because the monsters they ask for were never added, and one is the record of the human heritage, not a quest) are being moved to this format by the converter modernuo-quests of tools/convert, in slices. The converted quests are the files scripts/quests/ml_*.lua (224 so far: the kill, collect, skill, delivery and escort quests, and those whose C# code only says a text or grants a flag whose creatures, items and NPCs have a template) with their own list, scripts/quests/ml_index.lua, and their texts are messages numbered as the client numbers them, from data/messages/<language>/ml_quests.toml. The thing an objective counts is its noun; a collected weapon or piece of armor names the templates a vendor sells and a craft makes, so what the player made counts. A quest item (a boss’s tail, a bridle) is matched only to a template of its own name: it is never taken for the ordinary thing that shares its graphic, and a quest that asks for one that has no template is skipped.

They are on the shard: scripts/quests/index.lua adds the ids of ml_index.lua to the five examples. Their givers and the NPCs the deliveries go to, 99 NPCs, are made by the converter modernuo-quest-givers from the C# classes of ModernUO (the name, the title, the race and the sex, the hues and the hair when the class fixes them, and what the NPC wears) into templates/mobiles/ml_quest_givers.toml: invulnerable NPCs that stay where they are, with the id of the class in lower case (tiana, lyle). They stand where ServUO’s spawn files put them (templates/spawns/<map>/ml_quest_givers.toml, 156 places: Heartwood and the Sanctuary on Trammel and Felucca, New Haven on Trammel, two in Malas and two in Ilshenar), one NPC to a place, back after 5 to 10 minutes when one is gone. They have the skills their classes set and teach them as the vendors do (the Train entries of the context menu, the word train, gold dropped on them), and when a player comes within their sense range (8 tiles) and has a quest to take from one, the NPC turns and says one of its lines to that player alone (the client’s texts, from scripts/quests/ml_shouts.lua), at most once a minute: the script is quest_giver (shipped scripts). They wear the smith hammer and the radiant scimitar of their classes. Their rewards are the bags and satchels of random loot (135 of the 224 quests): { loot = "ml_bag_of_treasure" } and the like, with the tables in templates/loots/ml_rewards.toml (a category of ModernUO’s lists is one table of the templates of its types together, each of the bag’s items one of five categories at random; a craftsman’s satchel is one item of the lists of Mondain’s Legacy for its craft). What ModernUO adds to the items (magic properties, talismans, recipe scrolls) is not made, and a named reward that has no template, nor does the plain item it derives from, is dropped. Left for later slices: the properties of the artifacts and the rest of the rewards, and the quests whose code does more than that. One difference to know: ModernUO offers the quest that follows one on its own when the chain does not say otherwise; here a quest that follows another is offered only after it, and a quest that repeats does so after its delay (a few minutes where ModernUO has a restart delay).

The monsters those quests ask to kill and the data of UOX3 lacks (38 of them: the plague beasts, the crystal daemons, the bosses of Bedlam, the Blighted Grove…) are made by the converter modernuo-creatures from ModernUO’s classes into templates/mobiles/ml_creatures.toml: the stats, skills (to 120 at most), resistances, sounds and gold of the creature, and the spell circles of a caster as ModernUO’s mage AI. The kinds of damage, weapon abilities, immunities, poison, what a creature wears, its title and the other hooks of a class are not made, and the magic items of its loot are not either; a creature with no armor of its own gets half its physical resistance. They spawn where ServUO’s spawn files put them (templates/spawns/<map>/ml_creatures.toml, 446 places, each spawner’s MaxCount shared between its creatures; a monster a quest asks for in a region, an orc or a titan in the Sanctuary, is placed by the spawners of ServUO inside that region, so that every area quest can be done); a creature that the spawn files of a map already have (most of those of Malas, Tokuno and Ter Mur come from ModernUO’s own spawners) is not placed again. Eight creatures that the data has under another name (the elementals, the terathan warrior, the ophidian knight and avenger) are matched to it.

The things the collect quests ask for and no data has (15 of them: a Thrasher’s tail, Gaman horns, a pixie leg, Red Death’s bridle…) are made by the converter modernuo-quest-items from ModernUO’s classes into templates/items/ml_quest_items.toml (graphic, hue, stack, weight, the name of the client’s text, and loot_type = "blessed" where the class is blessed, so that the item stays with its owner when it dies): only those that a monster of the data drops, because a quest whose item cannot be got is not converted (a few of the 15 are asked only by quests of ModernUO that are not converted yet, and their monsters drop them all the same, as ModernUO’s do, a bonded pet that dies and is raised again included). The monsters that drop them (ModernUO’s OnDeath(Container c), with its chance and amount, and what a monster inherits from the class it derives from when its OnDeath calls the base) have a loot table each in templates/loots/ml_quest_drops.toml, named by scripts/quests/ml_drops.lua: when a monster of that template dies, scripts/events/quest.lua rolls its tables into the corpse, whoever killed it. The tinkering tools, the bamboo chair (the straw chair of the carpentry) and the refresh potion (the red potion of the alchemy) are the data’s own. Items that are crafted, mined or found, a quest item with no known source, and the quests that ask for them are skipped. The items a quest delivers are made all the same (35 quest items in all), whether a monster drops them or not. The 26 skill-training quests of New Haven (raise Swordsmanship, Chivalry, Mining… to 50: one skill objective each) are converted; the items they give are named artifacts of ModernUO that derive from an ordinary item, and the reward is that plain item (a ringmail legs for the Bulwark Leggings, a smith’s hammer for the Hammer of Hephaestus): the properties are not made; the accelerated skill gain they start for 15 minutes is not made either. The trainers that derive from other NPC classes (a mage, a blacksmith, a vendor) are made from their own constructor and their InitOutfit, as plain NPCs without their shop. Four rewards are not a plain item and are made by hand: the bag of necromancer reagents is its five reagents (50 each), the bag of smoke bombs is 20 smoke bombs (an item that does nothing yet), the Clasp of Concentration is a plain silver bracelet and the necromancer’s spellbook is the book of the data; any other bag that a class fills is dropped, because the plain parent is an empty one. A skill objective is complete the moment the skill gets there, but no message says so: the server has no hook for a skill gain, so the player sees it in the quest log, or when the giver is next asked. This slice brought 18 quests: 12 with a delivery or a time limit and 6 collect quests that were waiting for a link of their chain. The 12 are the Frederic and Leon chain (a timed delivery of ten minutes), Regina’s ring and letter, the tuition forms of Aernya and Gorrow, the golden horn, the gift for Arielle, the map fragments, the letter for Ahie, and the rat hunt of the Sanctuary against the clock (an hour). The NPCs a delivery goes to are made by the givers converter as the givers are (it reads the npc of the deliveries), called as the quests call them (QuesterName, “Emilio (Britain)”). Five quests stay out because Iosep, the skeleton of Szandor and the battered bucket stand in no spawn file of ModernUO or ServUO, three have C# code of their own, Patience asks for miniature mushrooms that no source gives, and two chains wait for a first quest that is not migrated; the Heartwood chain of the Arcanist waits for Patience, whose mushrooms no source gives, and the Sanctuary one ends with The Human Blight, which grants the spellweaving flag. Arielle, a pixie that cannot be hurt, is an NPC here and drops nothing. The 24 escort quests are converted: the 12 of the towns (Yew, Vesper, Trinsic, Skara Brae, Serpent’s Hold, Nujel’m, Moonglow, Minoc, Magincia, Jhelom, Cove and Britain) and the 12 of New Haven (the alchemist, the bard, the carpenter, the tailor, the bank, the warrior, the mage, the mapmaker, the inn, the farm, the docks and the bowyer), 500 gold each. ModernUO hands them to the escortable townsfolk it spawns; here the Seekers of Adventure of the data give them (m_seeker and f_seeker, which the town spawns name through their lists, and seeker, which the spawns of New Haven name: the towns’ escorts to those outside the island of New Haven, the island’s to those on it), as a list of the destinations. The escort to Ocllo is not made, because ModernUO’s escortables do not offer it. Responsibility, the escort of Lissbet to the sheep farm, waits: Lissbet stands in no spawn file of ModernUO or ServUO. The escorts of the towns (not New Haven’s) grant the flag human_in_need and say the client’s 1074949, as ModernUO’s “a human in need” does. What ModernUO adds to an escort and is not made: the virtue of compassion, the fame a young player gets, and the escortables that are not Seekers (nobles, merchants, mages, a bride and a prisoner). The region of the New Haven farm was missing in data/regions/trammel.toml and is added. The overrides of ModernUO’s quest classes are read by strict patterns: GetRewards that only says a text of the client (SendLocalizedMessage), grants a flag (Spellweaving.AwardTo, PlayerContext.SummonFey = true) or skips the reward gump (ClaimRewards) is data, as OnAccepted that sets a flag (BedlamAccess) and RecordCompletion (the engine records every turn-in); Generate, which places decorations, is counted and not made; any other statement (Eureka teaches a smithing recipe) keeps the quest out as code. That brought 11 quests: the boons of Maul, the Huntsman and Bravehorn’s herd, the human boons (Ingenuity, Heave Ho, All Season Adventurer), the end of the Sanctuary chain and Alliance, and the Bedlam quests (Not Quite That Easy, Convince Me, Tuition Reimbursement). The scroll of Summon Fey that Alliance gives has no template and is dropped, as the book of Spellweaving of The Human Blight (the plain book of its parent is a book of magery). The iron maiden that leads into Bedlam, which Not Quite That Easy places in ModernUO, is a decoration of its own (templates/decorations/malas/bedlam.toml, the script bedlam_teleporter.lua): it lets in whoever accepted that quest. The NPCs that derive from a monster (Maul, Strongroot, the Huntsman, Arielle) take the body of its class. Still out: the quests that wait for an item no source gives (the miniature mushrooms, the sap of Sosaria, the bauble, the prismatic amber, the wind chimes, the minotaur artifact, the samples of corrupted water), Wisdom of the Sphynx (its pitcher must hold water), the Spellweaving chains that start with such an item, Fiendish Friends (a text the client does not have), and the quests of the givers that stand nowhere. To refresh the whole migration run, in this order, modernuo-creatures, modernuo-quest-items, modernuo-quests and modernuo-quest-givers, then modernuo-spawns for the maps of ModernUO (Converters).

“Go to a place”, the marking of quest items, the quest arrow for an objective, and the 33 ModernUO quests that are not migrated yet (see above).

Made with ❤️ by Tom

Buy me a coffee