Portraits
QuestLines can display an NPC portrait image above the dialogue box whenever a player opens a conversation. Portraits are opt-in and highly configurable — you can use convention-named files, explicit resource-pack paths, or let the plugin fetch a headshot automatically from the HyCitizens skin data.
How It Works
When a dialogue opens, QuestLines resolves a portrait for the NPC using the following priority chain (first match wins):
- Page-level override — a
Portraitfield set directly on the dialogue page. - NPC override — an explicit texture path stored per-NPC in
npcs.json. - Convention file — a PNG placed at
QuestLines/portraits/{npcId}.png(or{citizenName}.pngfor HyCitizens NPCs). - Skin API fallback — a headshot fetched asynchronously from
api.hytl.skinusing the citizen's skin recipe (HyCitizens only).
The resolved image is sent to the player as a DynamicImageAsset (for local PNG files
and API fetches) or referenced directly as a resource-pack texture path (for override strings
that don't match a local file).
The portrait is rendered as a 320×320 px panel anchored above the dialogue box, on whichever side is configured. It is automatically released when the dialogue closes.
Global Configuration
Three keys in QuestLines/QuestLines.conf control portrait behaviour globally:
| Key | Default | Description |
|---|---|---|
portrait_enabled |
false |
Master toggle. Portraits are not shown unless this is true. |
portrait_side |
left |
Which side of the dialogue the portrait appears above. Accepts left or right. |
portrait_skin_fallback |
true |
When true and no portrait file is found, QuestLines fetches a headshot
from the skin API for HyCitizens NPCs. Set to false to disable all
outbound API calls.
|
Example QuestLines.conf section:
portrait_enabled=true
portrait_side=left
portrait_skin_fallback=true
Changes take effect after /ql reload.
Convention Files
The simplest way to add a portrait is to drop a PNG into the plugin's portraits directory. No further configuration is needed.
- Directory:
QuestLines/portraits/ - File name:
{npcId}.pngwherenpcIdis the NPC's key innpcs.json(citizen UUID or entity UUID). - Spaces in the ID are replaced with underscores — e.g. an NPC with display name
Bandit Captainmaps toBandit_Captain.png.
For HyCitizens NPCs, if no file is found for the citizen's ID, QuestLines also checks
for a file named after the citizen's display name
(e.g. Guard.png). This makes it easy to share one portrait across
multiple citizens that share the same name.
Portrait files are loaded once and cached in memory. Run /ql reload to
clear the cache and pick up new or replaced files.
There is no enforced resolution requirement, but portraits are displayed at 256×256 px by the skin API and rendered at 320×320 px in the UI. Square images at 256×256 or larger look best.
NPC Portrait Override
You can pin a specific texture path to an NPC in npcs.json
using the NpcPortraits map. The value can be either:
-
A local file name — QuestLines looks for
QuestLines/portraits/{value}.png(spaces replaced with underscores). If found, it is delivered as aDynamicImageAsset, the same as a convention file. -
A resource-pack texture path — if no matching local file exists,
the value is passed directly as a
TexturePathin the UI command. Use this to reference textures already bundled in your resource pack.
{
"NpcPortraits": {
"f48f7eaa-512e-415d-bfdb-f53d2ea0b991": "elder_mage",
"b2c3d4e5-...": "textures/npcs/blacksmith"
}
}
This override takes priority over convention files and the skin API, but is still
overridden by a page-level Portrait field.
Per-NPC Portrait Toggle
You can disable portraits for a specific NPC even when they are globally enabled.
Set NpcPortraitEnabled to false for that NPC's ID in
npcs.json:
{
"NpcPortraitEnabled": {
"f48f7eaa-512e-415d-bfdb-f53d2ea0b991": false
}
}
Only false values are stored. An absent entry means portraits are
enabled for that NPC (the default). This flag is ignored if the global
portrait_enabled setting is false.
Page-Level Portrait Override
Individual dialogue pages can override the portrait via the Portrait field
in the page's JSON. This is the highest-priority portrait source and bypasses all
NPC-level configuration.
{
"PageId": "p1",
"Npc": "Welcome, traveller!",
"Portrait": "elder_mage_angry",
...
}
The value follows the same local-file-first logic as the NPC override: QuestLines checks
for QuestLines/portraits/{value}.png first, and if not found, passes the
value through as a resource-pack texture path.
Use page-level overrides to change the portrait mid-conversation — for example, to show a different expression on a key emotional beat.
Skin API Fallback
When portrait_skin_fallback=true and HyCitizens is active, QuestLines will
automatically fetch a headshot for any citizen that has no portrait file configured.
The headshot is a 256×256 px PNG generated from the citizen's skin data.
Two fetch paths are used depending on the citizen's skin configuration:
-
Live-skin player-model citizens — a simple GET request to
api.hytl.skin/headshot/{username}. -
Character-builder citizens and custom-skin player-models — a POST
request to
api.hytl.skin/headshotwith the citizen's full skin recipe as a JSON body.
Fetches are asynchronous. On first open the portrait slot will be empty; once the fetch completes the dialogue is automatically refreshed to display the image. Subsequent opens for the same NPC use the cached result instantly.
The skin API fallback makes outbound HTTP requests to api.hytl.skin.
If your server has no internet access or you want to avoid the dependency, set
portrait_skin_fallback=false in QuestLines.conf.
Cache & Reload
Portrait data (both file bytes and API responses) is held in memory after the first load. This means disk reads and API calls happen at most once per NPC per server session.
To force a cache clear — for example after replacing a portrait PNG on disk — run:
/ql reload
The reload also re-reads QuestLines.conf, so you can change
portrait_enabled, portrait_side, or
portrait_skin_fallback and have the changes take effect without restarting
the server.
Quick Reference
| Where | What to set | Priority |
|---|---|---|
QuestLines.conf |
portrait_enabled=true |
Master toggle (lowest) |
QuestLines/portraits/ |
Drop {npcId}.png or {citizenName}.png |
Convention file (3rd) |
npcs.json NpcPortraits |
Map NPC ID → file name or texture path | NPC override (2nd) |
Page JSON Portrait field |
File name or texture path | Page override (highest) |
| Automatic | Skin API headshot (HyCitizens only, async) | Fallback (4th) |