EndCore Framework

en-quests

Quest-giver survivors with story quests, side jobs and daily tasks, tracked server-side from real game events.

en-quests gives your settlements survivors with stories. Quest givers stand around the map. When one has something for you, they show a map blip and a marker. Talk to them to open a dialogue with the camera on the speaker. They offer three kinds of quest:

  • Story quests: done once, often chained together.
  • Side jobs: done once.
  • Daily tasks: repeatable after a cooldown.

A quest is a list of steps: talk to someone, deliver items, carry items, reach a place, kill zombies (optionally a specific type), search bodies, or craft something. A tracker shows the current step and its progress. The journal (F5) lists active and completed quests, and lets you set a map waypoint or abandon a quest.

The server works out all progress from real game events, so a client can never simply claim a step is done. The one exception is "reach", which the server checks again. Kills and searches can also count for nearby party members on the same step. Rewards can be cash, character XP, items and skill XP.

At a glance

Depends onoxmysql, en-core, en-ui
Works withen-inventory, en-skills, en-party, en-radialmenu, en-zombies, en-crafting
Database tablesencore_quest_progress
Config filesconfig/shared.lua, data/npcs.lua, data/quests.lua
Key bindsen_quests_journal (F5), en_quests_talk (E, only when en-target isn't running)
Radial menuen-quests:journal under survivor
In-game builderContent kinds questnpc and quest in en-admin

How it works

Offers

A survivor offers a quest when all of these are true:

  • the quest isn't already active
  • a story quest or side job hasn't been completed before
  • a task's cooldown since its last completion has passed
  • you meet the character level, skill level and prerequisite quests in requires
  • you have fewer than Config.maxActive quests active

When several quests are on offer, the survivor says Config.offersLine and you pick one. Offers are ordered by Config.types.

Objectives

Step typeCompleted by
talkTalking to the named survivor
deliverHanding the items to the survivor (they're taken)
collectCarrying the items (you keep them)
reachArriving within radius of coords; the server checks within radius + 5 m
killen-zombies:server:zombieKilled, optionally a specific zombie type
searchen-zombies:server:corpseLooted
craften-crafting:server:crafted, matching a recipe id or result item

Abandoning and rewards

Abandoning a repeatable task you've completed before sets it back to completed, so the cooldown still applies. Otherwise it becomes abandoned. Reward items that don't fit in your inventory aren't given, and you get a "No room for ..." message.

Changing quest definitions

Editing a quest keeps player progress, clamped to the new number of steps. Deleting a quest leaves progress rows alone, and they resume if a quest with that id comes back.

Configuration

config/shared.lua

KeyDefaultWhat it does
Config.keys.journal'F5'Journal key bind
Config.maxActive3Most quests active at once
Config.talkDistance2.5Talk reach
Config.partyCredittrueKill and search credit for nearby party members
Config.giverMarkerstrueMarker over survivors with something for you
Config.blips{ sprite = 280, colour = 47, scale = 0.75 }Survivor blips
Config.dialogCameratrueCamera on the speaker
Config.offersLinestringWhat a survivor with several offers says first
Config.typesstory (order 1), side (2), task (3)Quest type labels and offer order

data/npcs.lua

A table keyed by survivor id. Defaults: rhodes, hale, vance.

FieldWhat it does
name, titleDisplay name and subtitle
modelPed model
coordsvec4
scenarioIdle scenario
greetingLines when they have nothing for you
bliptrue = always on the map; otherwise only while they have something for you

data/quests.lua

A table keyed by quest id (up to 60 characters). Quest definitions stay on the server; clients never see them. Defaults: first_steps, clean_water (needs first_steps), thin_the_herd, runner_hunt (level 5) and scrap_run (task, 20 h cooldown).

FieldWhat it does
label, descriptionJournal text
typestory, side or task
giverSurvivor id
offerLines spoken when offering
cooldownHoursTasks only; defaults to 24
requires{ level = n, skill = { name, level }, quests = { ids } }
rewards{ cash, xp, items = { { item, count } }, skills = { [skill] = xp } }
stepsOrdered list of steps

Every step has a type and label, and can have a hint (journal line) and coords (waypoint). Extra fields by type:

TypeFields
talknpc, dialog
delivernpc, item, count, dialog
collectitem, count
reachcoords (vec3, required), radius (default 10, 1–500)
killcount, zombie (optional type, e.g. 'runner')
searchcount
craftcount, and recipe (en-crafting recipe id) or item (result item)

Adding a survivor and a daily task

lua
-- data/npcs.lua
marla = {
    name = 'Marla', title = 'Grapeseed Farm',
    model = 'a_f_m_farmer_01', coords = vec4(2440.0, 4975.0, 46.8, 90.0),
    scenario = 'WORLD_HUMAN_GARDENER_PLANT',
    greeting = { 'Fields don\'t tend themselves.' },
},

-- data/quests.lua
cloth_for_marla = {
    label = 'Rags to Riches', type = 'task', giver = 'marla', cooldownHours = 12,
    description = 'Marla patches tarps for the farm and always needs cloth.',
    offer = { 'Bring me cloth and clear the walkers off the east field.' },
    requires = { level = 2 },
    steps = {
        { type = 'kill', count = 8, label = 'Clear the east field', coords = vec3(2470.0, 4990.0, 45.0) },
        { type = 'collect', item = 'cloth', count = 15, label = 'Gather cloth' },
        { type = 'deliver', npc = 'marla', item = 'cloth', count = 15, label = 'Bring Marla the cloth',
          dialog = { 'That\'ll hold the rain off. Here.' } },
    },
    rewards = { cash = 120, xp = 100, items = { { item = 'canned_beans', count = 2 } }, skills = { scavenging = 10 } },
},
Tip

For a step-by-step walkthrough, see Create a quest. Survivors and quests placed with en-admin override config entries with the same id, and deleting the placement brings the config version back.

Exports

All exports are server-side. There are no client exports.

ExportArgumentsReturns
IsQuestActivesource, questIdboolean
HasCompletedQuestsource, questIdboolean
AdvanceQuestsource, questId, amount?nil. On kill, search and craft steps it adds amount (default 1); on other steps it finishes the current step
BuilderListkindFor 'questnpc' or 'quest': { id, label, subtitle, coords, placed, inConfig }[]
BuilderGetkind, idEditable data
BuilderValidatekind, id, dataok, err. For quests it also checks that giver and step npc ids exist
BuilderSchemakinden-admin forms
BuilderTemplatekindDefaults for a new survivor or quest

Commands

CommandRestrictedWhat it does
/resetquest <target> <quest>group.adminResets that quest for the player (status abandoned, completions 0)

Events

Emitted, server-local:

EventPayload
en-quests:server:startedsource, questId
en-quests:server:stepChangedsource, questId, stepIndex
en-quests:server:completedsource, questId

Listened to, as objective sources:

EventUsed for
en-zombies:server:zombieKilledkill steps
en-zombies:server:corpseLootedsearch steps
en-crafting:server:craftedcraft steps
en-inventory:server:changedcollect and deliver tracking
encore:server:onPlayerLoaded, encore:server:onPlayerUnload, encore:server:levelChangedLoading progress and refreshing offers

Database

encore_quest_progress: primary key (citizenid, quest_id), plus status (active, completed or abandoned), step, count, completions, completed_at and started_at (unix times).

Examples

Use AdvanceQuest for an objective en-quests doesn't track itself. Here the current step of a signal_boost quest is finished when a player repairs a radio tower in your own script:

lua
-- server
AddEventHandler('myradio:server:towerRepaired', function(source)
    if exports['en-quests']:IsQuestActive(source, 'signal_boost') then
        exports['en-quests']:AdvanceQuest(source, 'signal_boost')
    end
end)

Gate content behind a finished story quest:

lua
if not exports['en-quests']:HasCompletedQuest(source, 'clean_water') then
    return false, 'Rhodes hasn\'t vouched for you yet.'
end

Announce completed quests:

lua
AddEventHandler('en-quests:server:completed', function(source, questId)
    print(('%s completed %s'):format(GetPlayerName(source), questId))
end)