Building AI-enabled NPCs
JediMUD has an AI service that lets NPCs respond dynamically to player input via an LLM. When a player types tell <npc> <message>, walks past an AI greeter, bows at an AI emoter, or gets hit by an AI taunter, the engine builds a prompt from the NPC’s personality + the situation, sends it to the AI backend, and delivers the response a couple seconds later in the NPC’s voice.
For builders, this means: any mob you make can have dynamic dialogue without writing a single trigger. You define the personality once; the AI handles the rest.
For the implementation details (worker queue, prompt sanitization, OpenAI/Ollama fallback chain, caching), see AI service on the staff docs.
The four MOB_AI_* flags
Set on the mob via medit <vnum> → A (NPC flags). Each flag enables one behavior:
| Flag | Behavior |
|---|---|
AI_ENABLED | Master switch — the mob responds to tell <npc> <msg> |
AI_GREETER | NPC greets PCs walking into the room (60s cooldown per NPC) |
AI_EMOTER | NPC reacts to socials targeting it (30s cooldown) |
AI_TAUNTER | NPC trash-talks PCs when it lands damage in combat (90s cooldown) |
You can combine all four — the wise oracle who greets, reacts to bows, taunts in combat, AND answers questions. Or pick the ones that fit the character.
AI_ENABLED is the gate for tell. Without it, all four flags do nothing — the master switch matters.
Cooldowns
The cooldowns are per-NPC — not per-player. If the Baker greets Foo at minute 0, he won’t greet Bar (or Foo) until minute 1. This is intentional: it makes the NPC feel like a person rather than a slot machine, and it keeps the API costs bounded.
Personality string
The AI’s response is shaped by the NPC’s personality — a short prose description of who they are, how they talk, what they care about. Stored in the ai_npc_personalities SQL table by mob vnum.
To add one, SQL is the canonical interface:
INSERT INTO ai_npc_personalities (mob_vnum, personality, enabled)
VALUES (3001,
'You are Hans the Baker, a kindly old man in Midgaard. You speak with warmth, occasionally reference the weather, and care deeply about quality flour. You distrust elves but try to hide it.',
1);
If no row exists for the mob, the AI uses a generic personality built from the mob’s short desc — usable but bland.
What makes a good personality
| Quality | Why it matters |
|---|---|
| 2-4 sentences max | Longer = the LLM forgets details and drifts. |
| Specific traits, not generic | ”Speaks with warmth, taps the counter when amused” beats “is friendly.” |
| One quirk per personality | An NPC who hates elves AND loves dwarves AND stutters AND grieves a dead son AND… is confused. Pick one. |
| In-fiction context | Reference the zone, the era, the local politics. “Forty years in this fountain square” places the NPC in time. |
| No meta-instructions | Don’t write “respond in one line” or “stay in character.” The prompt template handles that. |
Examples
Good:
You are Gertrude, the Midgaard fountain-keeper. You have lived in this square for forty years and know every secret. You speak slowly, look people in the eye, and remember names. You have a soft spot for orphans.
Good:
You are Captain Brennar, the no-nonsense quartermaster of the City Watch. You speak in short clipped sentences, suspect everyone of something, and respect only competence. You served on the southern wall during the orc siege twenty years ago.
Bad — too long and meta:
You are an NPC in a fantasy MUD. You should always stay in character and respond in one line. You are a baker who is friendly. Try not to break the fourth wall. Be helpful but not too helpful. Respond to greetings warmly. Don't reveal that you are AI.
Bad — too generic:
You are a baker. You are friendly. You sell bread.
Editing personalities
There’s no in-game personality editor yet. Two paths:
- SQL directly:
UPDATE ai_npc_personalities SET personality = '…' WHERE mob_vnum = 3001; - Web staff page:
/staff/has links to the data; check with staff if a personality-edit UI exists for your tree.
After editing, changes take effect immediately for new prompts. Cached responses for the old prompt may keep coming back for up to an hour — clear the cache via ai cache clear (staff command) if you need immediate fresh responses.
Building an AI NPC step-by-step
1. Build the mob normally
medit <vnum> and set the basics (name, descriptions, position, level, etc.) just like any other mob. The AI doesn’t replace anything else — your stats / class / NPC flags all still apply.
2. Set the AI flags
In medit … A:
- Always set
AI_ENABLEDsotellworks. - Add
AI_GREETERfor a “comes alive when you walk in” feel. - Add
AI_EMOTERfor social interactions. - Add
AI_TAUNTERfor combat presence (only for fighting NPCs).
3. Write a personality
INSERT INTO ai_npc_personalities (mob_vnum, personality, enabled)
VALUES (
<vnum>,
'Your personality string here (2-4 sentences).',
1
);
4. Save the mob
Q → y in medit.
5. Test
load mob <vnum> spawn next to you
tell <name> hello try a direct conversation
bow <name> try a social if AI_EMOTER set
(walk out and back in for greeter)
(start a fight for taunter)
You should see responses arrive 1-3 seconds after the input. The lag is the LLM thinking.
Common patterns
A welcoming shopkeeper
The baker who greets, has personality, but doesn’t fight:
- Flags:
AI_ENABLED,AI_GREETER,AI_EMOTER - No
AI_TAUNTER— he’s not a combatant. - Personality: warm, talks about bread / the weather / passing customers.
A combat boss with menace
The dragon who taunts mid-fight:
- Flags:
AI_ENABLED,AI_TAUNTER - No
AI_GREETER— encounter-level mob; the entrance is dramatic enough. - Personality: arrogant, ancient, references the smallness of mortals.
A quest-giver oracle
The seer who answers questions:
- Flags:
AI_ENABLED,AI_GREETER - Personality: cryptic, references prophecies + the local lore. Don’t put quest information in the personality — the LLM will hallucinate quest steps inconsistently. Use DG triggers for the actual quest logic.
A guard with attitude
The city guard who scowls at trespassers:
- Flags:
AI_ENABLED,AI_EMOTER,AI_TAUNTER - Personality: cynical, distrustful, references their duty.
- Pair with a Quest_block spec proc for the actual gate-keeping behavior; AI handles the flavor.
What players see
When the AI fires, the NPC says, emotes, or tells back to the player. From the player’s perspective, it’s normal NPC speech — they don’t see “AI thinking…” or any indication the line is generated.
Latency is typically 1-3 seconds (LLM round trip). For combat taunts, there’s an instant audio cue (a grunt via the sound layer) so the player knows the NPC is engaging, then the spoken line arrives a moment later.
If the AI service is disabled or fails, the NPC falls back to:
- For
tell: silence, or the legacyStock_speechspec proc if attached. - For ambient hooks (greet/emote/taunt): nothing fires.
AI + DG triggers — they coexist
You can put AI flags AND DG triggers on the same mob. They don’t conflict — triggers fire first; if a trigger handles the event (returns 1), the AI hook doesn’t fire.
This means you can:
- Use a TELL trigger for specific keywords (“password”, “quest”, “help”) and let AI handle the rest.
- Use a GREET trigger for the first greeting (quest hook), and let the AI greeter handle subsequent visits.
- Override AI taunts for scripted combat phases (e.g. “phase 2: the boss summons minions and roars”).
In general:
- AI = flavor, atmosphere, “this NPC feels alive.”
- Triggers = mechanics, quest state, conditional logic.
Use both for the best result.
When NOT to use AI
| Situation | Better tool |
|---|---|
| NPC needs to give a specific quest item on a specific phrase | DG TELL or RECEIVE trigger |
| Combat needs deterministic boss behavior (phase 2, enrage, summon adds) | DG HITPRCNT trigger or spec procs |
| Shop dialogue (buy/sell prompts) | Shop_keeper spec proc |
| Quest accept/info/complete flows | Questmaster spec proc + qedit |
| Dialogue that must be word-for-word reproducible | DG SPEECH trigger with fixed responses |
| Anything where token cost or rate-limit pressure matters | Plain triggers (free) |
AI is great for atmosphere. It’s bad for anything that needs to be exactly the same every time, or that the game logic needs to consume.
Cost & rate awareness
The AI service has a rate limit (per-minute + per-hour caps on OpenAI calls). When the rate limit is hit, requests fall back to a local Ollama instance (slower, smaller model, but free) — and eventually to silent fallback.
A busy zone with many AI greeters can burn through rate limit budget fast. Some discipline:
- Don’t put
AI_GREETERon every NPC in a crowded city — pick a few flavor characters. - Use cooldowns to your advantage — frequent re-entries to the same room don’t re-trigger the greeter for 60 seconds.
- Combat zones with many taunters multiply the load — concentrate taunters on bosses, not minions.
The system is designed to be lossy when overloaded (silent fallback rather than queueing), so over-flagging won’t crash anything — just produces less talkative NPCs as the budget runs out.
See also
- AI service — staff-side technical deep dive: API integration, async handoff, security, SQL schema.
- Building mobiles — the main mob-building guide; AI is a layer on top.
- Building triggers — for deterministic / scripted behavior to pair with AI.
- Spec procs (catalog) — service NPCs (
Shop_keeper,Questmaster,Postmaster) that handle mechanics; AI adds flavor on top.