Shops and quests
Giving an NPC a shop or quests, and adding your own kinds of quest task.
The Interactable NPC tool (Q menu > Tools > RPG X) gives a spawn point's NPC a role players talk to by pressing E on it: a shop or a quest giver. Its statues show like the Enemy Spawns tool's.
| On the toolgun | Does |
|---|---|
| Left click | Give the aimed-at point (its statue, or its live NPC) the role set up in the menu. After Set waypoint, sets the waypoint instead. |
| Right click | Copy the point's role into the menu. |
| Reload | Take the role away: a plain enemy again. |
The role stays when the point's enemy settings are changed with the Enemy Spawns tool; deleting the point deletes it. The menu remembers what's set up between sessions, and after applying it keeps what was saved, so editing and applying again to the same point keeps its quests (and players' progress in them). Applying to another point makes copies.
The NPC isn't made friendly: pick "Hostile to players" off on its point if it should be.
Shop: tick the weapons it hands out (every spawnable SWEP, searchable). Players see each one with its icon and name, like in the inventory. Free for now; Take puts one in your inventory.
Quest giver: any number of quests (up to 16). Each quest has:
- a name, a minimum player level (default 1; everyone is level 1 until there are levels), repeatable (can be taken again after turning it in) and a reward (a weapon, into the inventory on turn-in; there must be room),
- quests it needs finished first: other quests of this giver or of any giver on the map (quests waiting on each other in a circle are refused),
- tasks (up to 32). A task can wait for earlier tasks of the same quest ("go to the yard" before "kill 5 zombies"); until then nothing counts towards it.
Built-in task types:
| Type | Options | Done when |
|---|---|---|
| Kill X | Enemies that count (any NPC of those types, not just spawn point ones), how many, each (that many of every one picked; off: in total) | Enough are killed |
| Go to | A waypoint (Set waypoint closes the menu; your next left click is the spot), a radius, an optional place name | You're inside the radius |
While the tool is out, the menu's waypoints show as blue spheres. Players press E on the giver for a window with each quest's status (available, locked and why, in progress, ready to turn in, done), its tasks and progress, and Accept, Abandon and Turn in buttons. With a quest in progress, progress pops up as it's made. Abandoning resets a quest's progress.
The quest journal is the inventory's Quests tab (I): every accepted quest, who gave it, whether it's in progress or ready to turn in (and where to), its reward, and each task with its progress (✔ done, • open, ○ after an earlier task). One quest at a time is tracked (Track / Stop tracking): it shows on the HUD on the right with its tasks and progress, and the places of its open Go to tasks are marked on screen with the distance. Accepting a quest with nothing tracked tracks it; turning in or abandoning the tracked one leaves nothing tracked. The tracked quest is saved with the player's quests.
Task types from addons #
Quests are built from task types, and addons add their own the same way the built-in ones are
made (gamemode/sh_tasktypes.lua is the example):
hook.Add( "RPGXTaskTypes", "MyAddon", function( register )
register( "spin", {
name = "Turn in circles",
Defaults = function() return { turns = 3 } end,
Sanitize = function( data ) return { turns = math.Clamp( tonumber( data.turns ) or 3, 1, 50 ) } end,
Describe = function( data ) return "Turn around " .. data.turns .. " times" end,
Done = function( data, progress ) return ( progress.turns or 0 ) >= data.turns end,
ProgressText = function( data, progress ) return ( progress.turns or 0 ) .. " / " .. data.turns end,
Events = {
spun = function( data, progress, ply ) progress.turns = ( progress.turns or 0 ) + 1 return true end,
},
BuildEditor = function( parent, data, changed, tools )
local turns = parent:NumSlider( "Turns", nil, 1, 50, 0 )
turns:SetValue( data.turns )
turns.OnValueChanged = function( _, v ) data.turns = math.floor( v ) changed() end
end,
} )
end )
-- and wherever the addon notices a full turn (server):
GAMEMODE:QuestEvent( ply, "spun" )
Register on both realms. Events return true when they changed the progress; built-in events are
npc_killed( npc, npcName ) and think (twice a second). Optional: Waypoint( data ) -> pos, radius
marks a spot on the player's screen. A task type's options and a player's progress are stored as
JSON, so a new type needs no tables. A task type that errors is logged once and skipped, never
breaking the rest.