twinded_lgw_npc
A non-networked NPC spawner for RedM. Spawn peds locally to avoid consuming network entity slots, with distance-based spawn/despawn, native RedM prompts, map blips, animations, scenarios, text3d, and a full exports API.
Dependencies
| Resource | Required | Notes |
|---|---|---|
| twinded_libs | Yes | Free shared library |
Compatibility
Framework-independent — this script does not use any framework. It only requires twinded_libs for utility modules (prompts, blips, text3d, entities).
Installation
bash
ensure twinded_libs
ensure twinded_lgw_npcConfig Files
| File | Description |
|---|---|
settings.lua | Spawn delay, distance check interval, text3d config, debug toggle |
lang.lua | Translation strings (debug messages) |
text3d.lua | Custom text3d handler (when Config.Text3D.type = 'custom') |
npcs/examples.lua | Example NPC definitions (shopkeeper, guard, ambient) |
See the Configuration guide for how to override these files.
Configuration Reference
settings.lua
| Option | Type | Default | Description |
|---|---|---|---|
Config.SpawnDelay | number | 200 | Delay (ms) between staggered NPC spawns |
Config.DistanceCheckInterval | number | 2000 | How often (ms) to check player distance for spawn/despawn |
Config.Text3D.type | string | "default" | 'default' for built-in text3d, 'custom' for your own handler |
Config.Text3D.scale | number | 0.30 | Text scale |
Config.Text3D.background_alpha | number | 180 | Background opacity (0-255) |
Config.Debug | boolean | false | Enable debug commands |
NPC Definition Fields
Each NPC in Config.NpcList or in npcs/*.lua files:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier |
name | string | Yes | Display name |
coords | table | Yes | {x, y, z, heading} |
look.model | string | Yes | Ped model name |
look.outfit | table | No | Outfit components |
look.scale | number | No | Ped scale |
spawn_radius_in_meters | number | Yes | Distance at which NPC spawns |
face_player | string or nil | No | "full" (body) or "head_only" |
prevent_place_on_ground | boolean | No | Prevent ground placement (for indoor NPCs) |
weapon | string or nil | No | Weapon model name (e.g., "WEAPON_REVOLVER_CATTLEMAN") |
scenario | string or nil | No | RedM scenario name (e.g., "WORLD_HUMAN_STAND_BAR") |
animations | table or nil | No | Array of {anim_dict, anim_name, loop, anim_only_upper_body, times, delay_in_ms} |
text3d | table or nil | No | {text, radius, offset_z} |
blip | table or nil | No | {icon, default_color, scale, title, modifiers, time_window_color} |
prompts | table or nil | No | Array of prompt pages (see below) |
time_windows | table or nil | No | Array of {start_hour, start_minute, end_hour, end_minute} |
on_spawn | function or nil | No | Callback when NPC spawns |
on_despawn | function or nil | No | Callback when NPC despawns |
Prompt Page Fields
| Field | Type | Description |
|---|---|---|
page_label | string | Prompt group title |
page_radius_in_meters | number | Distance to show this prompt page |
page_prompts | table | Array of {label, key, hold, callback} |
text3d.lua
Custom text3d handler (when Config.Text3D.type = 'custom'):
lua
function DrawNpcText3D(options)
-- options.x, options.y, options.z — world coordinates
-- options.text — text to display
-- options.npc_id — NPC identifier
-- options.npc_name — NPC display name
endFeatures
- Non-networked peds — Zero network entity consumption
- Distance-based spawn/despawn — NPCs spawn when player is within radius
- Anti-pop despawn — NPCs in line of sight are not despawned until hidden
- Staggered spawning — One at a time with configurable delay
- Native RedM prompts — Multi-page prompt system with press/hold support
- Map blips — Configurable blips with time-window color changes
- Animations & scenarios — Looping animations, multi-step sequences, RedM scenarios
- Text3D — Floating text above NPCs
- Face player — Full body or head only rotation
- Attached props — Attach objects to NPC bones
- Time windows — NPCs visible only during specific in-game hours
- Exports API — 10 exports for inter-script integration
- Runtime NPCs — Register/unregister dynamically from other scripts
- Callbacks —
on_spawnandon_despawnhooks per NPC
Debug Commands
Set Config.Debug = true to enable:
| Command | Description |
|---|---|
/npc_debug | List all NPCs with state, distance, and animation status |
/npc_debug <npc_id> | Detailed info for a specific NPC |
/npc_respawn <npc_id> | Force respawn a specific NPC |
/npc_respawn_all | Respawn all currently spawned NPCs |
Exports API
Read
lua
-- Get all currently spawned NPCs
local npcs = exports['twinded_lgw_npc']:GetSpawnedNpcs()
-- Get entity handle of a specific NPC
local entity = exports['twinded_lgw_npc']:GetNpcByID('my_npc')
-- Check if a NPC is currently spawned
local spawned = exports['twinded_lgw_npc']:IsNpcSpawned('my_npc')Actions
lua
-- Force spawn/despawn regardless of distance
exports['twinded_lgw_npc']:ForceSpawnNpc('my_npc')
exports['twinded_lgw_npc']:ForceDespawnNpc('my_npc')
-- Override animation
exports['twinded_lgw_npc']:SetNpcAnimation('my_npc', 'anim_dict', 'anim_name', true, false)
-- Override scenario
exports['twinded_lgw_npc']:SetNpcScenario('my_npc', 'WORLD_HUMAN_STAND_BAR')
-- Enable/disable prompts
exports['twinded_lgw_npc']:SetPromptEnabled('my_npc', false)Dynamic NPCs
lua
-- Register a new NPC at runtime
exports['twinded_lgw_npc']:RegisterNpc({
id = 'my_custom_npc',
name = 'Custom NPC',
coords = { x = 0.0, y = 0.0, z = 0.0, heading = 0.0 },
look = { model = "a_m_m_rancher_01" },
spawn_radius_in_meters = 100,
})
-- Unregister and despawn
exports['twinded_lgw_npc']:UnregisterNpc('my_custom_npc')Troubleshooting
| Problem | Solution |
|---|---|
| Script doesn't start | Make sure twinded_libs is started before |
| NPCs not spawning | Check spawn_radius_in_meters and your distance to the NPC |
| NPCs stuck in ground | Set prevent_place_on_ground = true (common inside custom buildings) |
| Prompts not showing | Verify the NPC has a prompts field and page_radius_in_meters |
| Debug commands not working | Check Config.Debug is true |
| Text3D not visible | Verify text3d field with text and radius, and you're within range |

