Race-, gender-, alignment-, and service-aware NPC hail, callout, and combat voiceover libraries.
Lua script module · Reviewed October 22, 2026.
The files matching SpawnScripts/Generic/Generic*VoiceOvers.lua are shared Lua libraries for NPC hails, proximity callouts, service greetings, and combat speech. They select among existing voice assets using the NPC's race and gender and, in some libraries, the city zone, NPC faction, or the player's city faction standing.
These libraries are companions to NPCModule, not imports of it. NPCModule initializes combat statistics and selected appearance behavior; it does not load or call a voiceover library. A spawn script that needs both must load and invoke both explicitly.
Loading and calling
Load the one voice library that matches the NPC's role, then call its public helper from the applicable callback. Existing content normally uses dofile for these libraries:
require "SpawnScripts/Generic/NPCModule"
dofile("SpawnScripts/Generic/GenericMerchantVoiceOvers.lua")
function spawn(NPC)
NPCModule(NPC)
end
function hailed(NPC, Spawn)
FaceTarget(NPC, Spawn)
GenericMerchantHail(NPC, Spawn)
end
Loading a file alone does not call most of its helpers. The spawn script must invoke GenericMerchantHail, GenericEcologyHail, or the corresponding service function. Function names are global and case-sensitive. Use the lowercase engine callback names such as spawn, hailed, aggro, death, killed, and healthchanged; the helper name can use its declared capitalization.
The Spawn argument is the player or other actor receiving the flavor. PlayFlavor keys belong to the exact MP3 asset. Do not copy key values from another race, gender, optional voice set, or filename merely because its text is the same.
Library inventory
File
Public entry points
Current role
GenericVoiceOvers.lua
GenericHail(NPC, Spawn)
Short race/gender greeting-emote paths assembled at runtime.
GenericEcologyVoiceOvers.lua
Ecology, alternate-voice, racial, faction, drunk, and emote helpers
City atmosphere: direct hails and nearby-player callouts.
GenericMerchantVoiceOvers.lua
GenericMerchantHail(NPC, Spawn, faction)
Merchant greetings selected by race, gender, and the current zone's city alignment.
GenericBankerVoiceOvers.lua
GenericBankerHail(NPC, Spawn)
All 29 exact-key banker recordings in the supplied catalogue, selected by race, gender, city zone, player alignment, and city standing.
Race/gender combat speech. The file is marked work in progress.
The Faction/faction parameter is retained because it is part of the declared signatures and established call sites. It is not interpreted consistently by every service file: several current helpers select only by race/gender, the banker, armorsmith, merchant, and ecology libraries derive city context from zone IDs and player faction amounts, and the guard library reads the NPC's faction ID. Preserve the local call pattern when converting an existing NPC.
Selection and coverage
Most libraries begin with GetRace(NPC) and GetGender(NPC). Their branches use race IDs 0 through 18 and gender IDs 1 (male) and 2 (female). MP3 paths commonly encode the same choice with ecology and gender tokens such as _eco_good_, _eco_evil_, _gm_, and _gf_.
This is branch-selected coverage, not blanket coverage for every NPC. Many race/gender/alignment combinations are empty. A file name ending in VoiceOvers.lua, or a matching sentence somewhere in that file, does not prove that a particular NPC can play it. Confirm all of the following before treating a generic helper as coverage:
The spawn or quest script loads the intended file.
The applicable callback explicitly calls one of that file's helpers.
The helper has a reachable branch for the NPC's database race and gender.
Alignment, zone, faction, service role, and callback type match.
The exact MP3 path and both packet keys are present in that branch.
An empty branch remains a shared-library gap. Do not fall back to a female recording for a male NPC, another race's recording, or a good-city recording in an evil-city branch unless independent source evidence establishes that choice.
GenericMerchantVoiceOvers.lua and GenericEcologyVoiceOvers.lua currently classify Qeynos-oriented zone IDs as good and Freeport-oriented ranges as evil. Neutral and unlisted zones have narrower behavior. Review the actual zone checks before reusing these modules in a newer or instanced zone.
City citizenship for ecology audio requires both the player's alignment and positive standing with the matching city faction:
City
Player alignment
Citizen faction
Normal ecology
Otherwise
Freeport
0
12
faction amount > 0
matching notcitizen branch
Qeynos
1
11
faction amount > 0
matching notcitizen branch
Zero standing is not citizen standing. A player with zero or negative standing, or the opposing alignment, must not receive normal city ecology audio even if an older script checked only for a negative faction amount.
Ecology functions
GenericEcologyVoiceOvers.lua publishes these main groups:
Function
Intended use
GenericEcologyHail(NPC, Spawn, faction)
Basic direct hail.
GenericEcologyCallout(NPC, Spawn, faction)
Basic proximity callout.
GenericEcology2Hail / GenericEcology2Callout
Alternate voice sets for the races that have them.
GenericRaceCheckHail / GenericRaceCheckCallout
Race-aware hails and callouts.
FactionChecking / FactionCheckingCallout
Non-citizen responses used by the generic hail/callout paths.
GoodFactionEmotes / EvilFactionEmotes
Emotes toward opposing-faction players.
GenericDrunkHail / GenericDrunkCallout
Limited drunk/bar flavor, currently focused on Dwarves and Humans.
EcologyEmotes(NPC, Spawn)
Random flavor emotes.
The file uses shared timer flags and AddTimer reset functions to throttle repeated hail and callout behavior. Call the high-level hail/callout function unless a reviewed script specifically needs one of the specialized helpers. Faction-checking helpers are already called by the high-level ecology paths where applicable.
The private helper GetCityFactionAmount(Spawn, requiredAlignment, factionID) normalizes the player check used by those paths. Spawn is the player, requiredAlignment is 0 for Freeport or 1 for Qeynos, and factionID is 12 or 11 respectively. It returns the positive faction amount only when both conditions match; otherwise it returns -1, which sends the existing hail and callout branches to FactionChecking or FactionCheckingCallout.
The reviewed voice catalogue contains 265 exact-key notcitizen records. Generic helpers currently reference 234 of them, with every known non-service ecology record available in both the direct-hail and proximity-callout pools. GenericBankerVoiceOvers.lua contains all 29 exact-key banker recordings, including five Kerra-female Qeynos refusals and one Half-Elf-female Freeport refusal. GenericArmorsmithVoiceOvers.lua contains all 25 exact-key armorsmith recordings, including one Half-Elf-female and five Human-female Qeynos refusals. Kizmit and Renee Atherton load and invoke those helpers without embedding generic service MP3s in their spawn scripts. The remaining 31 records are 19 merchant, 5 weaponsmith, 5 pet retailer, 1 baker, and 1 innkeeper; no exact-key banker or armorsmith service candidates remain unwired in this catalogue. Add remaining recordings only to the corresponding service helper and race/gender branch; a banker or merchant refusal is not generic ecology dialogue.
A typical ecology script loads the library once, then keeps direct hail and proximity behavior separate:
dofile("SpawnScripts/Generic/GenericEcologyVoiceOvers.lua")
function spawn(NPC)
SetPlayerProximityFunction(NPC, 10, "InRange")
end
function hailed(NPC, Spawn)
FaceTarget(NPC, Spawn)
GenericEcologyHail(NPC, Spawn, faction)
end
function InRange(NPC, Spawn)
GenericEcologyCallout(NPC, Spawn, faction)
end
The lowercase variable faction in legacy examples may be unset and is not a universal configuration switch. If a script needs explicit faction behavior, inspect the selected helper rather than assuming this argument overrides its zone/faction checks.
Service functions
For a merchant, banker, guard, or tradeskill-service NPC, keep reusable ecology/service MP3 pools in the corresponding generic library. Centralizing the branch preserves race/gender and citizenship selection and makes a missing combination visible in one place. The individual spawn script should load that role helper and delegate its fallback hail without repeating any generic service MP3 paths. Kizmit.lua therefore calls GenericBankerHail, while ReneeAtherton.lua calls GenericArmorsmithHail.
When adding a verified service candidate, add the exact filename, text, emote, and both packet keys to the matching role/race/gender branch in the helper. Gate normal Qeynos service audio with both alignment 1 and faction 11 > 0; Freeport uses alignment 0 and faction 12 > 0. Do not borrow a sibling voice set to fill an empty branch.
The banker helper currently has normal Qeynos pools for female Barbarian, female Human, female Kerra, and male Erudite bankers; normal Freeport pools for female Dark Elf, male Erudite, and male/female Human bankers; a female Kerra Qeynos refusal pool; and a female Half-Elf Freeport refusal pool. The armorsmith helper currently has normal Qeynos pools for female Barbarian, female Dwarf, female Halfling, and male/female Human armorsmiths, plus female Half-Elf and female Human refusal pools. Unsupported combinations return without substituting another speaker.
These two helpers store each branch as a compact literal {file, text, emote, key1, key2} pool and dispatch one randomly selected row through PlayFlavor. The zone catalogue parser recognizes this exact five-field form as auditable voice evidence; do not make filenames or packet keys dynamic.
Service helpers are hail behavior. They do not create merchant inventory, set an NPC's faction, offer quests, initialize NPCModule, or replace quest-specific conversations. A quest-bearing service NPC may call a generic hail only in the fallback branch where no quest dialogue is active.
Keep quest eligibility and progress checks ahead of the generic fallback:
function hailed(NPC, Spawn)
FaceTarget(NPC, Spawn)
if HasQuest(Spawn, QUEST_ID) then
QuestConversation(NPC, Spawn)
else
GenericMerchantHail(NPC, Spawn)
end
end
Do not add a generic call before a quest conversation merely to make every hail voiced; that can play unrelated service audio on top of authored quest dialogue.
Combat functions
GenericCombatVoiceOvers.lua defines four lowercase helpers:
generic_aggro(NPC, Spawn) for encounter start.
generic_death(NPC, Spawn) for the NPC's death callback.
generic_killed(NPC, Spawn) after the NPC kills another spawn.
generic_healthchanged(NPC, Spawn) for health-threshold speech.
Load it and delegate from the matching callback only when that race/gender branch exists. The file declares itself work in progress, so missing combat branches are expected and should be catalogued rather than replaced with unrelated audio.
Audit and repair rules
When auditing a zone, treat a generic library as established coverage only when the consumer both loads the file and calls a published helper. Count the library's internal MP3 branches once as possible coverage; do not report every unused race/gender branch as a missing MP3 for every caller.
For an unvoiced NPC line that matches generic service text:
Identify the service library and the NPC's database race/gender.
Inspect the exact good/evil/neutral branch reached in that zone.
Reuse the shared helper if that branch already contains the exact MP3 and key pair.
If the correct branch is empty, record a library coverage gap. An asset path in an unknown-key catalogue is not sufficient to construct PlayFlavor safely.
If the line is quest-specific, keep it in the quest conversation and require actor/zone/stage evidence before wiring audio.
The zone dialogue audit under tools/dialogue_audit follows this policy and records loaded helper functions separately from explicit consumer invocations.
Reviewed October 22, 2026 against the local working tree. Source revision, reviewed files, and checksums. Full server source and voice assets remain local; examples are source-checked and have not been executed in a live world.
Definitions come from the reviewed Lua scripts and engine handlers. Server scripts and bindings are unchanged by this documentation update.