# Ambient NPCs Source: https://docs.humalike.com/ai-npc/ambient-npcs Dynamic AI characters Humalike spawns as networked pedestrians near players. Ambient NPCs are dynamic characters Humalike puts on the street itself: when players gather, the resource spawns a networked pedestrian of the character's model nearby, out of sight, and Humalike keeps the character on it for as long as players are around. ## Lifecycle 1. The planner groups nearby players and decides which characters should have a body around each group, and where. 2. The resource spawns the pedestrian at a safe, out-of-view spot and binds it. 3. Humalike tracks the character while that ped exists and drops the binding when the game removes it (a vanished body is released, never a stale lease). 4. Once no player has been near the body for the despawn time, it is removed; the character gets a new body the next time players are around. The character identity and memory are stable; a game entity handle is not. Your integration should refer to the Humalike NPC id whenever it stores character state. ## Configure ambient population Open a server in the [dashboard](https://humalike.ai/ai-npc) and use its **Ambient NPCs** settings to enable the system and tune the limits. Dashboard policy is authoritative for the number of active ambient characters and bodies. ## Models A character's ped model is the body it is spawned in, and the character decides it. Every game ped model is available — the 500-odd ambient, service, gang and unique pedestrians, not only the ones the game streets on its own. Every member of a [world](/ai-npc/dashboard#worlds) carries one to three **look tags** that say how they dress — `wealthy`, `office`, `street`, `beach`, `homeless`, a `worker:` tag for a uniform (`worker:pilot`, `worker:mechanic`) or a `gang:` tag for a street gang's colours (`gang:ballas`, `gang:lost_mc`). The proposal picks them from the role; you can change them on the proposal before generation, and afterwards through an edit request ("put Officer Diaz in an LSPD uniform"), which re-picks the body. A uniform or gang tag stands alone, and many uniforms exist for one sex only in the game — the pick list says which (`worker:paramedic` is men only, `worker:nurse` women only); a tag the member's sex cannot wear is refused on the proposal and dropped by the generator. The body is drawn uniformly among the catalogue models of the member's sex that carry one of their tags (nearest age band first), so a pilot wears the pilot uniform and a Ballas member the purple — nothing is off-limits, and a member whose tags match no body gets an ordinary civilian one. Members of one group get distinct bodies while the catalogue has them; the outfit follows the same tags on the models photographed so far, and the game's own outfit roll dresses the rest. The group's home zone decides where a character prefers to appear, not what it looks like. For a hand-made ambient character, give it any model the server has. ## How it works There is nothing to choose. While Ambient NPCs are **enabled**, Humalike owns the street: it spawns bodies for your dynamic characters near groups of players (a pedestrian of the character's model, within the spawn radius, out of sight, preferring the character's own home zone) and fills the rest of the street with **extras** — persona-less pedestrians drawn from every ordinary civilian model in the catalogue, whatever the zone, up to **Extras per cluster** around each group and **Extras limit** server-wide. Clients switch the game's own on-foot population off meanwhile (vehicles, traffic and parked cars are untouched; random police stay on when the `humalike_population_cops` convar is `true` — see [Configuration](/ai-npc/configuration#ambient-population)). Everything that applies to an ambient character — outfit, labels, voice, actions, injuries — applies to its body. Extras never speak or bind; set extras per cluster to `0` to run with characters only. **Disabled** means no dynamic characters and the game's crowd as it always was. A body is released once no player in its routing bucket has been within the body despawn distance for the body despawn time (the despawn distance must be at least the spawn distance, or bodies would be released as they land); the resource then removes it. Wounded or held bodies are kept until they are free again. Character bodies count against the map limit and the per-cell limit; extras count only against their own limit. **Per cell** does two things with one number: it caps the bodies Humalike holds in any one grid cell (of **Cell metres** a side), and, scaled by the zone's foot traffic, it is how many characters a group of players meets at once around them. The budget is shared so nobody gets a crowd while someone else gets an empty street. Characters are placed first. The extras limit is then split across groups of players by head count — players standing together share one allowance, and a bigger group gets a bigger share — and every group is capped by **Bodies per player scope**, the most Humalike bodies allowed around any one player. New bodies arrive at **Spawns per tick** per group, spread around the group rather than in one spot, so a crowd trickles in rather than lands. Where the group stands matters as much as the dials. Every zone carries a foot-traffic density from 0 to 1 by its reviewed character, and the per-group ceilings scale by it: a downtown pavement, a beachfront or a civic square gets the full per-cell limit and extras per cluster; a small town half of that; a desert road or an industrial yard a rare walker; a mountain range, a forest or the open sea nobody at all. Any zone with foot traffic keeps room for one character, so a persona homed in Paleto Bay can still be met there. Characters generated from a [world](/ai-npc/dashboard#worlds) carry their group's home zone, so they appear where they live — a preferred neighbourhood, not a pin: a character is planned in its home zone first, and elsewhere when players are not there. A hand-made character has no home zone and can be spawned anywhere; the API's `create_npc` / `update_npc` accept a `home_zone` (a game zone code such as `DAVIS`) to give it one — the dashboard sets it only through a world's groups. ## Drivers Some characters arrive by road: now and then a dynamic character is spawned at the wheel of a car — or on a motorbike, for a biker look — and drives around like the rest of the traffic instead of walking. What they drive follows the look of their model (a gang rides or drives its own kind of vehicle, money drives something expensive, everyone else a plain sedan); extras never drive. A driver is an ordinary body under the same limits, spawn rate and release rules as any other, and talking to one makes it pull over and stay until you are done. Drivers are on by default and switched off with the `humalike_npc_vehicles` convar (see [Configuration](/ai-npc/configuration#ambient-population)); off, every character spawns on foot as before. ## Performance tuning Do not raise the spawn rate or the per-scope ceiling only to make a street fill a fraction of a second sooner. More bodies mean more networked entities for every client in range. If you have measured a problem, the relevant advanced settings are documented under [Configuration](/ai-npc/configuration). Change one value at a time and compare `humalike_status` and server performance before and after. ## Common behavior * An ambient NPC disappearing once players leave is expected. * Its next body may have a different network id. * Different clients still observe the same networked entity while it exists. * With Ambient NPCs disabled Humalike does not touch the game's own population. Use a [static NPC](/ai-npc/dashboard#static-npcs) when the physical character must always return to one exact place. ## Next * [Configure NPCs in the dashboard](/ai-npc/dashboard). * [Review advanced world tuning](/ai-npc/configuration#advanced-world-tuning). * [Troubleshoot a missing NPC](/ai-npc/operations#npc-is-missing). # Configuration Source: https://docs.humalike.com/ai-npc/configuration Configure provider selection, labels, injuries, and advanced world behavior. Humalike uses FXServer convars written with `set`. The resource sends the values players need to their game when they join, so no documented setting requires `setr`; an existing `setr` keeps working. Keep the license behind `set` only. Settings are read when the resource starts: after changing one, run `restart humalike`. The two exceptions, `humalike_population_cops` and `humalike_npc_vehicles`, are re-read every few seconds. ## Minimal configuration ```cfg theme={null} set humalike_license_key "ak_secret_replace_me" set humalike_debug 0 ensure humalike ``` The license is the only credential you configure. Runtime and voice credentials are obtained automatically. ## Integration selection Provider domains are independent: ```cfg theme={null} set humalike_player auto set humalike_inventory auto set humalike_dispatch auto set humalike_actions auto set humalike_interaction auto ``` Each value accepts: * `auto` — select the single highest-priority available provider. * A provider name — require that provider. * `none` — disable the domain. If equally ranked providers conflict, `auto` reports `ambiguous` instead of guessing. Explicitly select the intended provider. ## Interface language ```cfg theme={null} set humalike_ui_language en ``` | Setting | Default | Description | | - | -: | - | | `humalike_ui_language` | `en` | Language of the texts Humalike itself shows players: the `/voice` panel and its own prompts. `en`, `pl`, `es` or `fr`. | Revive, mortuary and wound labels are translated one by one through their own convars below. ## NPC labels ```cfg theme={null} set humalike_npc_labels_enabled true set humalike_npc_labels_distance 14.0 set humalike_npc_labels_height 0.98 set humalike_npc_labels_scale 1.0 set humalike_npc_labels_default_language en set humalike_npc_labels_show_default_language false ``` | Setting | Default | Description | | - | -: | - | | `humalike_npc_labels_enabled` | `true` | Show the canvas label above AI NPCs. | | `humalike_npc_labels_distance` | `14.0` | Maximum render distance in metres. | | `humalike_npc_labels_height` | `0.98` | Vertical placement relative to the ped. | | `humalike_npc_labels_scale` | `1.0` | Label scale multiplier. | | `humalike_npc_labels_default_language` | `en` | The language most NPCs speak. An NPC speaking another language gets a flag (or its language code) on its label. | | `humalike_npc_labels_show_default_language` | `false` | Include the language marker for the default language. | ## Ambient population ```cfg theme={null} set humalike_population_cops false set humalike_npc_vehicles true ``` | Setting | Default | Description | | - | -: | - | | `humalike_population_cops` | `false` | Keep the game's own random police on while Humalike owns the street population (see [Ambient NPCs](/ai-npc/ambient-npcs)); read every few seconds, so it can be flipped live. Everything else — limits, radii, extras — is set in the dashboard. | | `humalike_npc_vehicles` | `true` | Let dynamic characters appear at the wheel of a car or on a motorbike and drive around like traffic (see [Drivers](/ai-npc/ambient-npcs#drivers)). Read every few seconds; `false` spawns every character on foot and never a vehicle. | ## Wounded and medical gameplay ```cfg theme={null} set humalike_npc_wounded_enabled true set humalike_npc_wounded_duration 300000 set humalike_npc_death_chance 5 set humalike_npc_medic_jobs "ambulance,ems" set humalike_npc_police_jobs "police" set humalike_npc_wounded_require_duty true set humalike_npc_medical_dispatch true ``` | Setting | Default | Description | | - | -: | - | | `humalike_npc_wounded_enabled` | `true` | Enable the wounded/death flow. | | `humalike_npc_wounded_duration` | `300000` | Wounded window in milliseconds. | | `humalike_npc_death_chance` | `5` | Percent chance that a wound becomes fatal. | | `humalike_npc_revive_linger` | `30000` | Time the revived NPC remains protected from cleanup. | | `humalike_npc_revive_linger_radius` | `20` | Linger radius in metres. | | `humalike_npc_medic_jobs` | `ambulance,ems` | Comma-separated jobs allowed to treat NPCs. | | `humalike_npc_police_jobs` | `police` | Comma-separated jobs allowed to handle deceased NPCs. | | `humalike_npc_wounded_require_duty` | `true` | Require the matching job to be on duty. | | `humalike_npc_revive_duration` | `10000` | Treatment progress duration in milliseconds. | | `humalike_npc_mortuary_duration` | `5000` | Mortuary interaction duration in milliseconds. | | `humalike_npc_treatment_distance` | `6.0` | Server validation distance in metres. | | `humalike_npc_deceased_retention` | `900000` | Time to retain a deceased NPC in milliseconds. | The treatment interactions show these texts: | Setting | Default | | - | - | | `humalike_npc_revive_label` | `Revive` | | `humalike_npc_revive_progress` | `Treating patient` | | `humalike_npc_mortuary_label` | `Send to mortuary` | | `humalike_npc_mortuary_progress` | `Securing the body` | ### Jobs without a framework ESX, QBCore and Qbox answer job checks from the framework, and a custom player provider answers them with its own `HasJob`. Only the `standalone` player provider reads ACE: a job is then a grant on the player or their group, and a grant counts as on duty: ```cfg theme={null} add_ace group.ems humalike.job.ambulance allow add_ace group.police humalike.job.police allow add_principal identifier.license:0123456789abcdef group.ems ``` Set `humalike_npc_medic_jobs` to an empty string to let every player treat wounded NPCs. ## Medical dispatch | Setting | Default | Description | | - | -: | - | | `humalike_npc_medical_dispatch` | `true` | Allow medical dispatch reports. | | `humalike_npc_medical_dispatch_chance` | `100` | Percent chance to report an eligible incident. | | `humalike_npc_medical_dispatch_delay` | `10000` | Delay before reporting, in milliseconds. | | `humalike_npc_medical_dispatch_throttle` | `30` | Minimum interval between reports, in seconds. | | `humalike_npc_mortuary_dispatch` | `true` | Notify dispatch about mortuary handling. | Dispatch settings only have an external effect when a dispatch provider is selected. ## Wound display | Setting | Default | Description | | - | -: | - | | `humalike_npc_wounds_enabled` | `true` | Track structured wounds. | | `humalike_npc_wound_display` | `true` | Allow the wound card to be shown. | | `humalike_npc_wound_display_distance` | `3.0` | Maximum inspection distance. | | `humalike_npc_wound_display_height` | `0.0` | Vertical offset of the wound card above the ped, in metres. | | `humalike_npc_wound_screen_offset` | `0.075` | Screen-space offset of the wound card. | | `humalike_npc_max_wounds` | `6` | Maximum displayed wound entries. | | `humalike_npc_wound_min_damage` | `5` | Minimum damage used for a wound report. | The wound card texts, each its own setting: | Setting | Default | | - | - | | `humalike_npc_wound_title` | `Injuries` | | `humalike_npc_wound_none` | `No visible injuries` | | `humalike_npc_wound_deceased` | `Deceased` | | `humalike_npc_wound_region_head` | `Head` | | `humalike_npc_wound_region_torso` | `Torso` | | `humalike_npc_wound_region_left_arm` | `Left arm` | | `humalike_npc_wound_region_right_arm` | `Right arm` | | `humalike_npc_wound_region_left_leg` | `Left leg` | | `humalike_npc_wound_region_right_leg` | `Right leg` | | `humalike_npc_wound_kind_gunshot` | `Gunshot wound` | | `humalike_npc_wound_kind_stab` | `Stab wound` | | `humalike_npc_wound_kind_blunt` | `Blunt trauma` | | `humalike_npc_wound_kind_burn` | `Burn` | | `humalike_npc_wound_kind_explosion` | `Blast injury` | | `humalike_npc_wound_kind_fall` | `Fall injury` | | `humalike_npc_wound_kind_vehicle` | `Vehicle impact` | | `humalike_npc_wound_severity_minor` | `minor` | | `humalike_npc_wound_severity_serious` | `serious` | | `humalike_npc_wound_severity_critical` | `critical` | ## Pose behavior | Setting | Default | Description | | - | -: | - | | `humalike_npc_pose_abandon_ms` | `120000` | Release a held pose after this time without nearby players. | | `humalike_npc_pose_abandon_radius` | `60` | Nearby-player radius used by pose cleanup. | | `humalike_npc_pose_threat_watch` | `true` | Let threats interrupt sustained poses. | | `humalike_npc_pose_threat_radius` | `30` | Threat observation radius in metres. | | `humalike_npc_pose_threat_clear_ms` | `10000` | Time before a threat state clears. | ## Advanced world tuning The `humalike_world_*` convars control collection, batching, snapshots, and NPC reporting. Their defaults are designed for normal servers. Change them only to diagnose a measured issue; shorter intervals increase client, server, and network work. | Setting | Default | Description | | - | -: | - | | `humalike_world_tick_ms` | `20` | Collector scheduling tick. | | `humalike_world_moving_interval_ms` | `100` | Player update interval while moving. | | `humalike_world_idle_interval_ms` | `1000` | Player update interval while idle. | | `humalike_world_listener_interval_ms` | `33` | Local listener update interval. | | `humalike_world_npc_edge_enabled` | `1` | Enable NPC world streaming. | | `humalike_world_npc_edge_interval_ms` | `200` | NPC frame interval. | | `humalike_world_npc_edge_ticket_retry_ms` | `3000` | Retry interval after a failed NPC edge ticket. | | `humalike_world_npc_edge_ticket_refresh_ms` | `12000` | NPC edge ticket refresh interval. | | `humalike_world_npc_report_radius` | `150` | NPC report radius in metres. | | `humalike_world_npc_max_per_frame` | `32` | NPCs per frame, clamped to `1`–`32`. | | `humalike_world_voice_enabled` | `1` | Enable voice world updates. | | `humalike_world_voice_delta_batch_ms` | `100` | Voice delta batch interval. | | `humalike_world_voice_snapshot_ms` | `60000` | Full voice snapshot interval. | | `humalike_world_cabin_voice_enabled` | `1` | Enable vehicle cabin routing. | | `humalike_world_cabin_start_delay_ms` | `1000` | Delay before cabin routing starts, at least `0`. | | `humalike_world_cabin_retry_ms` | `2000` | Cabin routing retry interval, at least `500`. | | `humalike_world_cabin_snapshot_ms` | `30000` | Full cabin snapshot interval, at least `5000`. | | `humalike_world_snapshot_interval_ms` | `60000` | Server authority snapshot interval. | Do not configure undocumented endpoint, authority, or voice-secret convars. They are managed by the released runtime and are not part of the supported customer configuration surface. ## Next * [Select a framework](/ai-npc/frameworks). * [Configure gameplay systems](/ai-npc/gameplay). * [Look up every public interface](/ai-npc/reference). # Contributing Source: https://docs.humalike.com/ai-npc/contributing Propose fixes and extensions to the Humalike game resource without breaking its public contracts. The Humalike game resource accepts community pull requests. Focused bug fixes, tests, performance improvements, generic integrations, and documentation corrections are welcome. ## Discuss features first Talk to us on [Discord](https://discord.gg/7bZFjm9aHH) before implementing a change that: * adds a feature or configuration option; * changes NPC, voice, world, or integration behavior; * adds or changes a provider capability; * changes communication between the game resource and Humalike; * changes a public export, event, convar, command, or data contract; * adds a dependency or changes release packaging. This gives maintainers and contributors a chance to agree on the public contract before substantial implementation work begins. Small fixes, tests, and documentation corrections can go directly to a pull request. ## Preserve public contracts Do not independently change the resource-to-Humalike wire contract, authentication or licensing flow, runtime credential handling, service addresses, or protocol payloads. These changes require prior maintainer approval and a coordinated release. Treat versioned provider APIs, public exports, events, convars, and commands as compatibility contracts. Additive changes still need discussion. Breaking changes require an explicit migration plan. Keep the core resource standalone and framework-neutral. Customer-specific jobs, economy rules, permissions, private events, and UI hooks belong in a separate bridge. A generic framework integration must use public APIs, remain optional, and preserve standalone operation. Never commit credentials, license keys, private endpoints, customer details, deployment information, or generated runtime data. ## Submit a pull request 1. Branch from the current `main` branch. 2. Keep the pull request focused on one behavior or contract. 3. Add focused tests for behavior changes. 4. Update documentation when the supported public surface changes. 5. Explain the motivation, compatibility impact, and validation in the pull request description. Run the checks documented in the repository's contributing guide before submitting the pull request. Maintainers may ask to split a broad change or adjust its design to protect compatibility. ## Report security issues privately Do not publish vulnerabilities in an issue or Discord message. Use [security@humalike.ai](mailto:security@humalike.ai). # Dashboard and NPC setup Source: https://docs.humalike.com/ai-npc/dashboard Register servers and configure NPCs from the AI NPC dashboard. Open the [AI NPC dashboard](https://humalike.ai/ai-npc) to manage the product. Server configuration is kept in the dashboard; runtime behavior specific to the game resource is configured with convars. ## Servers Each registered server has its own callback URL, license, NPC roster, world, and usage. From the server list you can: * Register, enable, disable, or delete a server. * Update its public callback address. * Rotate its server license. * Open its NPC roster and ambient policy. Rotating a license invalidates the old value. Update `humalike_license_key` and restart the resource after rotation. ## Create an NPC An NPC contains both character design and in-game placement: * **Identity and personality** — name, biography, behavior, and knowledge. * **Language and voice** — spoken language and assigned voice. * **Type** — static, ambient, or external. * **Appearance** — the game ped model. For an ambient character this is the only body it ever binds to, so choose one the game spawns where you want it met. * **Placement** — coordinates and heading. Static NPCs only. * **Status** — enabled NPCs are included in the live roster. Use the voice preview before saving a production character. Keep the character's written language aligned with its selected spoken language. ## Static NPCs Static NPCs are created at their configured position and return through normal streaming and resource lifecycle changes. Use them for characters whose identity belongs to a place. Choose a valid pedestrian model and place the NPC on navigable ground. Avoid placing it inside map geometry or directly in vehicle traffic. ## Ambient NPCs Ambient NPCs are assigned to compatible population peds near players. Their identity persists while the underlying game ped may change. Use the server's ambient policy to enable the system and control its population. See [Ambient NPCs](/ai-npc/ambient-npcs). ## External NPCs External NPCs have no placement. Your resource spawns a ped with the configured model and binds the character to it. The NPC stays offline until bound. See [External NPCs](/ai-npc/integrations/external-npcs). ## Worlds Worlds connect NPCs through shared groups, relationships, events, and knowledge. They are optional: individual NPCs work without a generated world. Use a world when multiple characters should know about the same people, locations, or ongoing events. Every group in a proposal has a **home zone**, a game map zone such as Davis or Rockford Hills, and every member has a **sex** and an **age band**. All three can be edited before generation. Each member's ped model is then picked from the pedestrians the game naturally spawns in that zone, matching the body, so a group is met together in one part of the map and a persona always looks like its text says. A gang's own street models are used where the game streets them (in a stock multiplayer population, only the Lost MC has any); elsewhere gang members wear the zone's ordinary civilians. The models come from the stock game population files, see [ambient NPCs](/ai-npc/ambient-npcs#models). Each generated character also gets a fixed outfit and face among its model's built-in variants, chosen to fit the group from a catalogue of every piece each street model can wear: several characters can share a model and still look like different people, a character looks the same every time it is met, and its persona is written to match what it wears. ## Disable versus delete * **Disable** preserves the NPC and its configuration but removes it from live gameplay. * **Delete** permanently removes it. Use disable for maintenance and testing. ## Next * [Configure ambient NPCs](/ai-npc/ambient-npcs). * [Understand voice and direct targeting](/ai-npc/voice). * [Review NPC turn billing](/npc-turns). # Framework compatibility Source: https://docs.humalike.com/ai-npc/frameworks Use Humalike with standalone servers, ESX Legacy, QBCore, or Qbox. Humalike selects each integration domain independently. Installing a framework does not force its inventory or interaction system. | Host setup | Player provider | Inventory | Interactions | Roleplay actions | | - | - | - | - | - | | Standalone | `standalone` | Optional custom provider | Built-in prompt | Custom bridge | | ESX Legacy | `esx` | `esx` or `ox_inventory` | Prompt or `ox_target` | `esx_rpchat` when installed | | QBCore | `qbcore` | `qb_inventory` or `ox_inventory` | Prompt, `qb_target`, or `ox_target` | QBCore `/me` integration | | Qbox | `qbox` | `ox_inventory` | Prompt or `ox_target` | Qbox `/me` integration | ## Built-in priorities ### Player | Provider | Required resource | Priority | | - | - | -: | | `standalone` | None | `-1000` | | `esx` | `es_extended` | `100` | | `qbcore` | `qb-core` | `100` | | `qbox` | `qbx_core` | `100` | ### Inventory | Provider | Required resource | Priority | | - | - | -: | | `esx` | `es_extended` | `50` | | `ox_inventory` | `ox_inventory` | `100` | | `qb_inventory` | `qb-inventory` | `100` | ### Interaction | Provider | Required resource | Priority | | - | - | -: | | `builtin` | None | `-1000` | | `ox_target` | `ox_target` | `10` | | `qb_target` | `qb-target` | `10` | The ESX inventory is deliberately a fallback, so `ox_inventory` wins when both are available. If `ox_inventory` and `qb-inventory`, or `ox_target` and `qb-target`, run together at the same priority, select one explicitly. ```cfg theme={null} set humalike_player qbcore set humalike_inventory qb_inventory set humalike_interaction qb_target ``` ## Jobs and duty * QBCore and Qbox require `job.onduty == true` when an interaction requires duty. * ESX has no universal duty field. Membership passes unless the server's job object explicitly exposes a false `onduty` or `onDuty` value. * Standalone has no framework to ask, so a job is an ACE grant: `add_ace group.ems humalike.job.ambulance allow`. Empty job requirements pass; ACE has no duty state, so a granted job counts as on duty. A custom player provider replaces this with real job and duty state. ## Runtime changes Starting, stopping, or restarting a framework, inventory, target, or custom bridge causes provider selection to be evaluated again. Humalike does not need a full server restart. Run `humalike_status` after changing resources. Resolve `ambiguous`, `unavailable`, or `degraded` domains before testing gameplay. ## Custom or modified frameworks Built-in providers support the public behavior of the listed frameworks. A heavily modified fork may expose different exports, player data, duty state, or events. Connect it through a [custom bridge](/ai-npc/integrations/custom-bridge) instead of editing Humalike. ## Next * [Build a custom bridge](/ai-npc/integrations/custom-bridge). * [Read the provider API](/ai-npc/integrations/provider-api). * [Troubleshoot provider selection](/ai-npc/operations#provider-states). # Gameplay systems Source: https://docs.humalike.com/ai-npc/gameplay NPC actions, following, vehicles, injuries, jobs, inventory, dispatch, server-defined actions, and the shop counter. Humalike turns conversation decisions into validated in-game actions. Built-in actions cover common character behavior; providers connect server-specific inventory, dispatch, and gameplay systems. ## Built-in actions | Action | Result | | - | - | | `wave` | Play a short wave. | | `start_dancing` | Begin a sustained dance. | | `interrupt_animation` | Interrupt the current animation. | | `kneel` | Enter a sustained kneeling pose. | | `hands_up` | Raise hands. | | `punch` | Attempt a close-range punch. | | `stand_up` | Leave a seated or downed state. | | `follow_player` | Follow the requesting player. | | `stop_following` | Stop following. | | `hold_position` | Remain at the current position. | | `release_movement` | Return movement control to normal AI behavior. | | `enter_vehicle` | Enter a free seat in somebody else's vehicle: the player's, or the nearest one within reach. | | `enter_own_vehicle` | Walk back to the vehicle the NPC arrived in, take the wheel and drive off. Offered only while that vehicle stands nearby. | | `exit_vehicle` | Leave the current vehicle. | | `walk_away` | Move away and finish at a safe distance. | | `run_away` | Sprint away from the person and out of the scene. | `give_item` becomes available when an inventory provider is selected. Custom action providers can add server-specific action keys. ## Following and vehicles Only the relevant player relationship is used when prioritizing a following NPC. The NPC adjusts pace when it falls behind and can enter or exit vehicles through explicit actions. Following and vehicle state also influence [voice targeting](/ai-npc/voice). ## Inventory Inventory providers implement server-authoritative item delivery. Humalike validates the action and delegates the final item operation to the selected provider. A custom inventory provider must return `true` only after it has accepted the item. Never trust item names, quantities, or metadata from an unvalidated client event. Apply server-specific allowlists in your bridge when needed. ## Injuries and death When enabled, NPC damage produces structured wounds. An NPC may enter a wounded state, require treatment, or die according to server configuration. Authorized medic and police jobs receive interactions through the selected interaction provider. Treatment is validated on the server by player identity, job, duty state, distance, and the current NPC state. With the `standalone` player provider, jobs are ACE grants (`humalike.job.`); a custom player provider evaluates jobs itself. See [Jobs without a framework](/ai-npc/configuration#jobs-without-a-framework). ## Dispatch Medical incidents can call the selected dispatch provider after a configured delay and chance. Humalike supplies a neutral kind and payload; the bridge translates them to the server's dispatch resource. ## Server-defined actions An integration resource can declare its own actions, each gated on facts the server reports. The contract is in [Server-defined actions](/ai-npc/integrations/custom-actions). * Only reported observations unlock an action, and only for the player the NPC is answering. What a player says does not count. * While a condition is unmet the action stays listed, the tag is not accepted, and the NPC is told what has to happen first. Nothing reaches the server. * Conditions are checked again when the model writes the tag, so a fact that expired while it answered does not slip through. * A performed deed is written to the NPC's transcript as a world event naming the facts it spent. Spent facts are gone for every other deed and for the shop counter. * The gate reads the NPC's recent conversation plus its newest server-namespaced world events regardless of age, so a busy NPC forgets neither an unpaid order nor a spent fact. * A rejected deed (`RunAction` returned `false`) is not retried. The model may write the tag again on a later reply as a new deed with a new invocation id. ## Shop counter An actions provider with a `Catalog` turns an NPC into a shop. Humalike takes the order, prices it, tracks payment and calls the server to hand over goods or return money. The model never authors a price, a delivered quantity or a returned amount. 1. The model orders in one tag: `[myserver:order water=10 pistol=1]`. Humalike checks the shelf and per-item limits, prices the order and tells the NPC the total. 2. Payment is the reported payment observation of the catalogue currency, in whole units. 3. Once the money covers the total, Humalike calls `deliver` on the NPC's next reply to that player, records the sale and removes the lines from the shelf. 4. `[myserver:cancel_order]` calls `refund` with what is on the counter. It wins over a pending delivery in the same reply. | Case | Result | | - | - | | Underpaid | Nothing moves. The NPC is told what is owed. | | Order refused (unsold item, no stock, over a limit) | Recorded with its reason. The NPC reads it on its next turn. | | `deliver` returns `false` | Nothing recorded. Order and money stay. Retried on the next reply to that player. | | Shelf ran out before `deliver` | Not attempted. The NPC is told and can cancel for a refund. | | `refund` returns `false` | Retried on the next reply. Goods do not go out meanwhile. | | `deliver` or `refund` unanswered | No retry and no cancel until it answers. The NPC is told the hand-over is pending. | | New order while one is open | Replaces it. Money on the counter counts towards the new total. | | Order older than one hour | Ages out. Money stays on the counter until a `deliver` or `refund` spends it. | | Menu order (`PlaceOrder`) while a paid order awaits hand-over | Refused and recorded as `order_refused` with reason `order_open`. | Retries of `deliver` keep the same invocation id while the same order stands, even after a top-up. A `refund` is identified by the order it cancels, or by the payments when no order is open. The dashboard shows the NPC's current shelf read-only. ## Custom behavior Keep rewards, economy changes, server-specific animations, dispatch codes, and job rules in a separate integration resource. See the [provider API](/ai-npc/integrations/provider-api). ## Next * [Configure medical gameplay](/ai-npc/configuration#wounded-and-medical-gameplay). * [Build a custom bridge](/ai-npc/integrations/custom-bridge). * [Add a custom action provider](/ai-npc/integrations/provider-api#action-descriptor). * [Declare server-defined actions and a shop counter](/ai-npc/integrations/custom-actions). # AI NPC Source: https://docs.humalike.com/ai-npc/index Add persistent, voice-enabled AI characters to a game server. Humalike adds voice-enabled AI characters to game servers. NPCs can hold conversations, remember players, react to events in the world, move with players, enter vehicles, and perform gameplay actions. The released `humalike` resource runs standalone. Optional integrations add framework identity, jobs, inventory, dispatch, roleplay events, and target UI without modifying the resource. Register a server, install the resource, and create your first NPC. Manage servers, NPCs, voices, worlds, and usage. Select integrations and customize labels, injuries, and gameplay behavior. Connect a custom framework without editing Humalike. ## NPC types | Type | How it appears | Best for | | - | - | - | | **Static** | Humalike creates and maintains a persistent ped at configured coordinates. | Named characters, businesses, quest characters, and fixed locations. | | **Ambient** | Humalike assigns a character to a compatible population ped already created by the game. | A living population distributed naturally around players. | | **External** | Your own resource spawns and owns the ped and binds the character to it. | Scripted scenes, mission peds, and NPCs whose lifecycle your framework controls. | All three types use the same conversation, memory, voice, and action systems. ## What the resource handles * NPC spawning, binding, movement, actions, injuries, and world observations. * WebRTC proximity voice, vehicle audio, and direct conversation targeting. * Automatic framework and inventory discovery. * Built-in interaction prompts with optional `ox_target` and `qb-target` support. * Versioned provider APIs for custom server integrations. ## Requirements * A current FXServer build with OneSync enabled. * A publicly reachable server callback address. * A Humalike AI NPC account and server license. * A working player microphone for voice conversations. No roleplay framework, voice resource, target resource, or inventory resource is required for standalone use. ## Next * [Quickstart](/ai-npc/quickstart) — talk to your first NPC. * [Installation](/ai-npc/installation) — production installation and updates. * [Framework compatibility](/ai-npc/frameworks) — ESX, QBCore, Qbox, and standalone. * [NPC turns](/npc-turns) — plans, metering, and the free trial. # Installation and updates Source: https://docs.humalike.com/ai-npc/installation Install, order, update, and roll back the game resource safely. ## Install a release Contact us on [Discord](https://discord.gg/7bZFjm9aHH) for the latest `humalike.zip` release. Do not download GitHub's automatic source archives: they contain repository files, not the composed runtime resource. Place the extracted `humalike` directory directly in a resources directory and start it by name: ```cfg theme={null} set humalike_license_key "ak_secret_replace_me" ensure humalike ``` The directory must not be nested as `humalike/humalike`. ## Resource order Humalike can start without a framework. For automatic selection during initial startup, start optional dependencies first: ```cfg theme={null} ensure oxmysql ensure ox_lib ensure es_extended # or qb-core / qbx_core ensure ox_inventory # optional ensure ox_target # optional ensure humalike ensure my-humalike-bridge ``` Late-starting and restarted dependencies are detected. A custom bridge should start after `humalike` and declare it as a dependency. ## Callback networking The callback URL registered in the dashboard must reach the FXServer from the public internet. Allow inbound TCP traffic to the selected server port. If you change the FXServer port or public address, update the callback in the dashboard. The URL format is: ```text theme={null} http://PUBLIC_IPV4:port ``` The host must be a globally routable literal IPv4 address. Hostnames, IPv6, private addresses, loopback addresses, and reserved example addresses are not accepted. Supported ports are `1024`–`65535`; do not add a path, query, or fragment. ## Update 1. Download the new release and verify its published SHA-256 checksum. 2. Stop `humalike` or stop the FXServer. 3. Keep the previous directory as a rollback copy outside the active resources tree. 4. Replace the complete `humalike` directory. 5. Start it and run `humalike_status`. Do not merge a new release over the old directory. Removed files would remain active and can produce mixed-version behavior. Keep custom code in a separate bridge resource. Updating Humalike should never overwrite server-specific jobs, dispatch logic, rewards, or UI hooks. ## Roll back Stop the resource, restore the complete directory from the previous release, and start it again. The server license remains valid. If the dashboard schema or required server behavior changed in a release, follow that release's notes before rolling back. ## Development builds Production servers should use release archives. Repository checkouts require a frontend build and are intended only for contributors. A release already contains all compiled NUI assets. ## Next * [Complete the quickstart](/ai-npc/quickstart). * [Review configuration](/ai-npc/configuration). * [Prepare production operations](/ai-npc/operations). # Server-defined actions Source: https://docs.humalike.com/ai-npc/integrations/custom-actions Declare deeds your own script performs, gated on the facts it reports. The actions provider declares actions beside the observations it [reports](/ai-npc/integrations/world-events#server-observations). The model chooses a declared action like any catalogue action, Humalike accepts the tag only once the required observations have been reported, and your `RunAction` performs the deed. How the gate and the shop counter behave in play is described under [Gameplay systems](/ai-npc/gameplay#server-defined-actions). ```lua theme={null} exports.humalike:RegisterProvider('actions', { name = 'my_actions', apiVersion = 1, priority = 100, SupportedActions = {}, Namespace = 'myserver', Observations = { item_given = { fields = { item = 'string', quantity = 'integer' }, template = { en = 'the character handed you {quantity} x {item}' }, }, }, Actions = { give_map = { name = 'Give the treasure map', description = 'Hand the player the map to the hidden chest.', params = { copies = { type = 'integer', enum = { 1, 2 } } }, fixed = { item = 'treasure_map' }, requires = { { observation = 'item_given', where = { item = 'amulet' }, consume = true }, }, locked_hint = { en = 'Only once the amulet is in your hands.' }, }, }, RunAction = function(action, source, npcCoords, params) if action ~= 'give_map' then return false end return exports.ox_inventory:AddItem(source, params.item, params.copies or 1) end, }) ``` Enable `myserver:give_map` on the NPCs that should have it in the dashboard. Keys are namespaced on the wire (`myserver:give_map`) and local in `RunAction` (`give_map`). `RunAction` receives the model's `params` merged with `fixed` and `player_id`: ```lua theme={null} -- Tag: [myserver:give_map copies=2] -- RunAction receives ('give_map', source, npcCoords, { player_id = 12, copies = 2, item = 'treasure_map' }) ``` Actions are re-declared on every capability report. A changed or removed action takes effect on the next resource start. ## Declaration | Field | Description | | - | - | | `name` | Shown in the dashboard. Up to 80 characters. | | `description` | The line the model reads under its ACTIONS rules. Up to 400 characters, no square brackets. | | `params` | Up to 4 values the model fills in. See [Params](#params). | | `fixed` | Up to 16 values your script decides. Never sent to Humalike; merged under the model's values before `RunAction` and always win. | | `requires` | Up to 4 conditions on reported observations, all of which must hold. See [Requirements](#requirements). | | `locked_hint` | What the NPC is told, per language code (any language the dashboard offers; English is read when the NPC's language has no text), while a condition is unmet. | | `limit` | `{ per_player = 1, every_s = 86400, hint = { en = '...' } }`. Deeds per player in any window of `every_s` seconds (up to 7 days). No limit unless declared. | | `auto` | `true`: Humalike performs the deed on the NPC's next reply once its conditions hold, whether or not the model writes the tag. Requires a `consume = true` condition. | | `params_from` | `{ amount = 'item_given.quantity' }`. Values taken from the unlocking facts, never from the model. See [Params from facts](#params-from-facts). | | `uses_stock` | `{ item = 'map', quantity = 1 }`. The deed takes from the NPC's shelf and locks at zero. See [Stock](#stock). | ### Params ```lua theme={null} params = { copies = { type = 'integer', enum = { 1, 2 }, required = true, description = 'How many copies.' }, } ``` * Types are `string`, `integer` or `boolean`. * `enum` holds up to 16 bare words or integers. * `required` and `description` are optional. * `player_id` is always added by Humalike. * A tag whose values do not fit the declaration is dropped before it is spoken or remembered. ### Requirements ```lua theme={null} requires = { { observation = 'item_given', where = { item = 'cash', quantity = { gte = 500 } }, within_s = 600, consume = true }, } ``` | Field | Description | | - | - | | `observation` | A declared observation key, reported for this NPC and the player it is answering. | | `where` | Conditions on declared fields. All must match. | | `within_s` | Age limit of the counted facts in seconds. 5 to 3600, default 600. | | `consume` | `true` spends every fact the condition counted once the deed is done. | | `where` form | Matches | | - | - | | `item = 'amulet'` | Scalar equality. `1` and `1.0` are the same number. | | `quantity = { gte = 500 }` | Lower bound on a numeric field. | | `quantity = { lte = 3 }` | Upper bound. `gte` and `lte` combine. | | `quantity = { sum_gte = 2 }` | Sum across all counted facts. | Spent facts are gone for every deed and for the shop counter. ### Params from facts ```lua theme={null} refund_deposit = { name = 'Give the deposit back', description = 'Return the deposit the player put on the counter.', requires = { { observation = 'item_given', where = { item = 'cash' }, consume = true } }, params_from = { amount = 'item_given.quantity' }, }, -- RunAction receives { player_id, amount = } ``` * Values come from the facts counted by the **first** `requires` entry on that observation. * Numbers are summed over those facts. Anything else is the newest value. ## Stock ```lua theme={null} local result = exports.humalike:SetNpcStock(npcId, { map = 50, bread = 'unlimited' }) ``` Replaces the NPC's whole shelf. An item not listed is one the NPC has none of. The dashboard shows the shelf read-only on the NPC's page. * Up to 32 items per NPC. * Item names match `^[a-z][a-z0-9_]*$`, at most 48 characters. * Counts are whole numbers from 0 to 1,000,000, or `'unlimited'`. Returns `{ ok = true, value = { stock } }` or `{ ok = false, error = code }`: | Error | Meaning | | - | - | | `invalid_npc` | `npcId` is not a UUID string. | | `npc_not_found`, `npc_not_bound` | Same as `ReportObservation`. | | `invalid_stock` | `stock` is not a table. | | `invalid_item:` | Name breaks the pattern or length. | | `invalid_count:` | Count is not a whole number in range or `'unlimited'`. | | `too_many_items` | More than 32 items. | | `runtime_not_ready` | No credentials yet. Nothing was sent. | ## Shop counter For NPCs that sell, declare a `Catalog` instead of one action per product. Humalike takes the order, prices it and collects payment, then calls `deliver` and `refund` on your `RunAction`. ```lua theme={null} Catalog = { currency = 'cash', payment = 'item_given', items = { water = { price = 5 }, bread = { price = 3 }, pistol = { price = 150, limit = { per_player = 1, every_s = 86400 } }, }, }, ``` | Field | Description | | - | - | | `currency` | The `payment` observation's `item` value that counts as money. | | `payment` | The declared observation your inventory hook reports. Its `quantity` must be typed `integer`. | | `items` | 1 to 64 products, named by the stock's rule, each with a whole `price` from 0 to 10,000,000 and an optional `limit` (same shape as an action `limit`). | An NPC sells the catalogue items your script [stocked](#stock). ### `deliver` and `refund` Both are declared for you with the `Catalog`. Do not list them under `Actions`. | Action | `params` | Return | | - | - | - | | `deliver` | `items` (item to quantity), `total`, `paid`, `change`, `currency`, `player_id` | `true` only after every line and the change are in the player's inventory. On any failure undo what was added and return `false`. | | `refund` | `amount`, `currency`, `player_id` | `true` after the amount is back in the player's inventory. | ```lua theme={null} local function giveAllOrNothing(source, items, currency, change) local given = {} local function undo() for name, count in pairs(given) do exports.ox_inventory:RemoveItem(source, name, count) end end for name, count in pairs(items) do if exports.ox_inventory:AddItem(source, name, count) ~= true then undo() return false end given[name] = count end if change > 0 and exports.ox_inventory:AddItem(source, currency, change) ~= true then undo() return false end return true end RunAction = function(action, source, npcCoords, params) if action == 'deliver' then return giveAllOrNothing(source, params.items, params.currency, params.change) elseif action == 'refund' then return exports.ox_inventory:AddItem(source, params.currency, params.amount) == true end return false end ``` ### Menu orders ```lua theme={null} local result = exports.humalike:PlaceOrder(npcId, playerId, { water = 2 }) ``` Sends picked lines from your own shop menu. Up to 16 lines, each quantity 1 to 1,000. Returns `{ ok = true, value = { lines } }` or `{ ok = false, error = code }`: | Error | Meaning | | - | - | | `invalid_player`, `character_not_loaded`, `npc_not_found`, `npc_not_bound` | Same as `ReportObservation`. | | `no_catalog` | The actions provider declares no `Catalog`. | | `invalid_lines` | `lines` is not a non-empty table. | | `unknown_item:` | Not in the catalogue. | | `invalid_quantity:` | Not a whole number from 1 to 1,000. | | `too_many_lines` | More than 16 lines. | ### Reserved keys * `order`, `cancel_order`, `order_placed`, `order_refused`: not declarable as actions or observations. * `deliver`, `refund`: not declarable as observations. * No key may be both an action and an observation. * `spent` is a reserved observation field. ## RunAction contract * Return exactly `true` when the deed is done. `false` rejects the invocation and is not retried. * Answer within 5 seconds. A lost answer (timeout, dropped connection, HTTP 5xx) is retried at once, up to three attempts, under the same invocation id. * Keep the ids you accepted and answer a repeat from that record, so a lost reply never runs a deed twice. ## Debugging * A refused declaration is printed by `RegisterProvider` with the field at fault. Nothing is registered. * The dashboard's NPC page lists declared actions with their conditions. The transcript view shows observations and deeds. * `humalike_debug 1` logs every inbound push and its result. ## Next * [How the gate and the shop counter behave](/ai-npc/gameplay#server-defined-actions). * [Report the observations these actions depend on](/ai-npc/integrations/world-events#server-observations). * [Review the provider contract](/ai-npc/integrations/provider-api). # Build a custom bridge Source: https://docs.humalike.com/ai-npc/integrations/custom-bridge Create a separate game resource that connects your framework to Humalike. This example registers a custom player provider and a client interaction provider. Add only the domains your server owns. ## Directory ```text theme={null} resources/ ├── humalike/ └── my-humalike-bridge/ ├── fxmanifest.lua ├── server.lua └── client.lua ``` ## Manifest Add the following entries to your bridge's existing `fxmanifest.lua`. Keep its required game and runtime declarations unchanged. This is a manifest fragment, not a complete manifest. ```lua theme={null} name 'my-humalike-bridge' version '1.0.0' dependency 'humalike' server_script 'server.lua' client_script 'client.lua' ``` Start it after Humalike: ```cfg theme={null} ensure humalike ensure my-humalike-bridge ``` ## Server provider ```lua theme={null} local function registerProviders() local ok, err = exports.humalike:RegisterProvider('player', { name = 'my_framework', apiVersion = 1, priority = 100, Available = function() return GetResourceState('my-core') == 'started' end, GetCharacterId = function(source) local player = exports['my-core']:GetPlayer(source) return player and player.characterId or nil end, GetCharacterName = function(source) local player = exports['my-core']:GetPlayer(source) return player and player.fullName or nil end, IsCharacterLoaded = function(source) return exports['my-core']:GetPlayer(source) ~= nil end, HasJob = function(source, jobNames, requireDuty) local player = exports['my-core']:GetPlayer(source) if not player then return false end for _, name in ipairs(jobNames) do if player.job.name == name then return not requireDuty or player.job.onDuty == true end end return false end, Notify = function(source, message, kind) TriggerClientEvent('my-ui:notify', source, message, kind) end, }) if not ok then print(('[my-humalike-bridge] player provider failed: %s'):format(err)) end end AddEventHandler('onResourceStart', function(resource) if resource == GetCurrentResourceName() then registerProviders() end end) AddEventHandler('humalike:integration:ready', registerProviders) ``` Do not cache a player object across callbacks. Resolve the active character from `source` each time so reconnects and character switches are handled correctly. ## Client interaction provider ```lua theme={null} local entries = {} local function registerInteraction() exports.humalike:RegisterInteractionProvider({ name = 'my_target', apiVersion = 1, priority = 100, Available = function() return GetResourceState('my-target') == 'started' end, watchedResources = { 'my-target' }, Add = function(id, entity, options) entries[id] = { entity = entity, options = options } exports['my-target']:AddEntity(entity, options) return true end, Remove = function(id) local entry = entries[id] if not entry then return end exports['my-target']:RemoveEntity(entry.entity) entries[id] = nil end, Progress = function(durationMs, label) return exports['my-progress']:Run(durationMs, label) == true end, }) end AddEventHandler('onClientResourceStart', function(resource) if resource == GetCurrentResourceName() then registerInteraction() end end) AddEventHandler('humalike:integration:ready', registerInteraction) ``` Each option contains `text`, optional `icon`, `canInteract(entity)`, and `onSelect(entity)`. Preserve those callbacks when adapting the option to your target system. ## Select and verify ```cfg theme={null} set humalike_player my_framework set humalike_interaction my_target ``` Run `humalike_status` in both the server and client consoles. Both domains should show `selected`, the expected provider name, and the bridge as owner. See [Provider API](/ai-npc/integrations/provider-api) for every descriptor and return value. ## Next * [Add neutral player events](/ai-npc/integrations/world-events). * [Check provider status](/ai-npc/operations#provider-states). * [Review integration security](/ai-npc/security#trust-boundaries). # External NPCs Source: https://docs.humalike.com/ai-npc/integrations/external-npcs Bind a Humalike character to a ped your own resource spawns and owns. An **external** NPC is a dashboard character whose ped is spawned, moved and deleted by your resource. Humalike attaches the identity to your entity and never spawns, positions, freezes or deletes it. To take over the AI of an NPC Humalike itself spawned, see [Runtime control](/ai-npc/integrations/runtime-control). All exports on this page return one result table: `{ apiVersion = 1, ok = true, value = ... }` or `{ apiVersion = 1, ok = false, error = code }`. They must be called from a resource other than `humalike`, or they fail with `external_resource_required`. ## Create the character In the dashboard, create the NPC with type **External** and the ped model your script will spawn. No placement is required. Mobility actions may be enabled. The NPC stays offline until a resource binds an entity to it. ## Bind an entity ```lua theme={null} local result = exports.humalike:BindNpcEntity(npcId, networkId, { routingBucket = 0 }) if not result.ok then return print(result.error) end local binding = result.value -- { id, npcId, networkId, routingBucket, ownerResource } exports.humalike:UnbindNpcEntity(binding.id) ``` | Argument | Type | Description | | - | - | - | | `npcId` | string | The external NPC's id. | | `networkId` | integer | Network id of a ped your resource owns. | | `options.routingBucket` | integer, optional | Refused if it differs from the entity's actual bucket. | The entity must exist, be a non-player ped, use the NPC's configured model and carry no other Humalike identity. One binding per NPC and per entity. Repeating the same bind from the same resource returns the existing binding. Unbind removes Humalike state and leaves the ped untouched. | Error | Meaning | | - | - | | `invalid_npc_id`, `invalid_network_id`, `invalid_options`, `invalid_routing_bucket` | Argument shape. | | `npc_not_found` | Not on the synced roster. | | `npc_not_external` | The NPC is static or ambient. | | `entity_not_found`, `entity_not_ped`, `player_ped_not_allowed` | The network id does not resolve to a non-player ped. | | `model_mismatch` | The ped's model differs from the NPC's configured model. | | `routing_bucket_mismatch` | `options.routingBucket` differs from the entity's bucket. | | `entity_already_humalike` | The ped already carries a Humalike identity. | | `npc_ownership_conflict` | Another resource has bound this NPC. | | `entity_ownership_conflict` | This entity is bound to another NPC. | | `binding_not_found`, `not_owner` | `UnbindNpcEntity` with an unknown id or another resource's binding. | ## Lifecycle * Your bindings are detached when your resource stops. * A binding is dropped when the entity disappears, changes model or routing bucket, or when the NPC's type changes in the dashboard. * Humalike emits the server event `humalike:npc:ready` with `{ apiVersion, generation }` after the first roster sync of each runtime generation. A resource that starts later misses it. Bind on your own start and again on every ready event. A bind attempted before the first roster sync fails with `npc_not_found` and succeeds on the event: ```lua theme={null} local function bindAll() for npcId, record in pairs(myPeds) do exports.humalike:BindNpcEntity(npcId, record.networkId) end end AddEventHandler('onResourceStart', function(resource) if resource == GetCurrentResourceName() then bindAll() end end) AddEventHandler('humalike:npc:ready', bindAll) ``` ## Despawn a static NPC ```lua theme={null} local despawn = exports.humalike:DespawnNpc(npcId) -- value = { id, npcId, ownerResource } local restored = exports.humalike:RespawnNpc(npcId) ``` Removes a static NPC's ped from the runtime without changing its definition. Only the despawning resource can respawn it, and its despawns are restored when it stops. | Error | Meaning | | - | - | | `npc_not_found`, `npc_not_static` | Not a static NPC on the roster. | | `runtime_binding_unavailable` | The NPC has no live runtime binding yet. | | `despawn_not_found`, `not_owner` | `RespawnNpc` for an NPC this resource did not despawn. | | `spawn_failed` | The ped could not be recreated. | ## Next * [Lease control of an NPC's AI domains](/ai-npc/integrations/runtime-control). * [Report facts about your NPC](/ai-npc/integrations/world-events#server-observations). * [Compact export reference](/ai-npc/reference#server-exports). # Integration overview Source: https://docs.humalike.com/ai-npc/integrations/overview Extend Humalike through independent, versioned provider domains. Humalike works standalone and keeps server-specific behavior behind providers. A provider is a Lua descriptor registered by a separate resource through a public export. ## Domains | Domain | Side | Responsibility | | - | - | - | | `player` | Server | Character identity, loaded state, jobs, duty, notifications. | | `inventory` | Server | Add an item to a character inventory. | | `dispatch` | Server | Translate a neutral incident into the server's dispatch system. | | `actions` | Server | Execute server-specific NPC actions. | | `interaction` | Client | Add/remove NPC interaction options and show progress. | Domains are deliberately independent. A player adapter must not also choose an inventory, dispatch, action, or interaction implementation. ## Selection Providers declare a name, API version, priority, and optional availability callback. `auto` selects the single available provider with the highest priority. A tie is `ambiguous`; Humalike does not guess. Provider callbacks run in the resource that registered them, so they can use that resource's local framework object and exports. ## Ownership and lifecycle * Provider names are owned by the registering resource. * A different resource cannot replace that provider name. * Re-registering the same name from the same resource is idempotent. * Stopping the owner automatically removes its providers. * Starting or stopping a watched dependency reevaluates availability. * Restarting Humalike creates a new runtime epoch and emits `humalike:integration:ready` on both server and client. Listen for the ready event and register again. This supports either resource starting first and makes individual restarts safe. ## What belongs in a bridge * Framework-specific player lookups and job rules. * Inventory calls and item allowlists. * Dispatch event translation. * Custom action validation and execution. * Target-system or UI integration. * Translation of server roleplay events to Humalike's neutral events. Do not copy Humalike internals or modify the released directory. Start with the [custom bridge guide](/ai-npc/integrations/custom-bridge). ## Next * [Build a custom bridge](/ai-npc/integrations/custom-bridge). * [Read the provider API](/ai-npc/integrations/provider-api). * [Report server roleplay events](/ai-npc/integrations/world-events). # Provider API Source: https://docs.humalike.com/ai-npc/integrations/provider-api Reference for server and client provider registration exports. The current provider API version is `1`. Provider names must be lowercase, at most 64 characters, and contain only letters, digits, `_`, `.`, or `-`. Priorities must be finite numbers between `-100000` and `100000`. ## Register a server provider ```lua theme={null} local ok, errorMessage = exports.humalike:RegisterProvider(domain, descriptor) ``` `domain` is `player`, `inventory`, `dispatch`, or `actions`. Registration must come from an external resource. It returns `true` on success or `false` plus a stable, human-readable reason when the descriptor is rejected. Every descriptor requires: | Field | Type | Description | | - | - | - | | `name` | string | Provider name used by its selection convar. | | `apiVersion` | number | Must be `1`. | | `priority` | number | Higher available priority wins in `auto`. | | `Available` | function, optional | Return `true` when dependencies are ready; optionally return `false, reason`. | ### Player descriptor | Callback | Required | Contract | | - | - | - | | `GetCharacterId(source)` | Yes | Return a stable active-character id or `nil`. | | `GetCharacterName(source)` | Yes | Return the active character's display name or `nil`. | | `IsCharacterLoaded(source)` | Yes | Return exactly `true` when gameplay state is ready. | | `HasJob(source, names, requireDuty)` | No | Return exactly `true` when one requested job matches and duty requirements pass. | | `Notify(source, message, kind)` | No | Display a notification to the player. | `kind` may be `success`, `error`, `warning`, or `inform`. Treat unknown kinds as informational. ### Inventory descriptor ```lua theme={null} exports.humalike:RegisterProvider('inventory', { name = 'my_inventory', apiVersion = 1, priority = 100, AddItem = function(source, itemName, quantity, metadata) return true end, }) ``` `AddItem` must return exactly `true` after accepting the operation. Return `false` for a full inventory, unknown item, invalid metadata, or any other rejection. ### Dispatch descriptor ```lua theme={null} exports.humalike:RegisterProvider('dispatch', { name = 'my_dispatch', apiVersion = 1, priority = 100, Report = function(kind, payload) return true end, }) ``` Translate the neutral `kind` and `payload` into your dispatch resource. Return `false` only when the report was rejected; `nil` counts as accepted after a successful callback. ### Action descriptor ```lua theme={null} exports.humalike:RegisterProvider('actions', { name = 'my_actions', apiVersion = 1, priority = 100, SupportedActions = { 'hand_over_money' }, RunAction = function(action, source, npcCoords, params) return true end, }) ``` Action keys must start with a lowercase letter, contain only lowercase letters, digits, and `_`, and be at most 64 characters. `RunAction` must return exactly `true` after completing or accepting the action; it is optional for a provider whose `SupportedActions` is empty. Validate `source`, distance from `npcCoords`, permissions, identifiers, amounts, and all `params` on the server. The action name is not authorization. The same descriptor declares the server observations the integration can report (see [Server observations](/ai-npc/integrations/world-events#server-observations)): ```lua theme={null} exports.humalike:RegisterProvider('actions', { name = 'my_actions', apiVersion = 1, priority = 100, SupportedActions = {}, Namespace = 'myserver', Observations = { item_given = { fields = { item = 'string', quantity = 'integer' }, template = { en = 'the character handed you {quantity} x {item}', pl = 'postać wręczyła ci {quantity} x {item}', }, }, }, }) ``` * `Namespace` matches `^[a-z][a-z0-9]{1,15}$` and is required as soon as `Observations` is non-empty. It prefixes every key on the wire (`myserver:item_given`), so a server key never spells a built-in event type. * `Observations` maps up to 32 keys (`^[a-z][a-z0-9_]{0,31}$`) to a definition with up to 8 `fields`, named like keys and typed `string`, `integer`, `number` or `boolean`, and a `template` with at least one of the languages the dashboard offers (the NPC's language is used, else English, else the first declared). Each line is at most 400 characters with no control characters, its `{placeholders}` name declared fields, and it contains no other braces. A template is also sized by its worst-case render -- every placeholder occurrence at its field's widest value (64 characters for a `string`, 21 for a `number`, 17 for an `integer`, 5 for a `boolean`) -- which must stay within 912 characters. The resource enforces all of this in `RegisterProvider`: a descriptor that breaks a rule is rejected with the reason, and nothing is registered. ## Unregister a server provider ```lua theme={null} local ok, errorMessage = exports.humalike:UnregisterProvider(domain, name) ``` Only the owner resource can unregister its provider. Stopping the owner performs this cleanup automatically. ## Server status ```lua theme={null} local status = exports.humalike:GetProviderStatus() ``` The result contains: * `apiVersion` and `runtimeEpoch`; * `selected`, a compact map by domain; * `domains`, including each setting, state, reason, selected provider, and all registered candidates. States are `selected`, `disabled`, `unavailable`, `ambiguous`, or `degraded`. A callback failure degrades the selected provider; a later successful callback clears the failure state. ## Register a client interaction provider ```lua theme={null} local ok, errorMessage = exports.humalike:RegisterInteractionProvider({ name = 'my_target', apiVersion = 1, priority = 100, Available = function() return true end, watchedResources = { 'my-target' }, Add = function(id, entity, options) return true end, Remove = function(id) end, Progress = function(durationMs, label) return true end, }) ``` `Add` and `Remove` are required. `Progress` is optional. `Add` must return exactly `true` when registration succeeds. `Progress` returns exactly `true` when completed and `false` when cancelled or failed. `watchedResources` lists dependencies whose start/stop should trigger provider reevaluation. ```lua theme={null} exports.humalike:UnregisterInteractionProvider('my_target') local status = exports.humalike:GetInteractionProviderStatus() ``` Client status reports `apiVersion`, `runtimeEpoch`, setting, state, reason, and the selected provider. ## Ready event ```lua theme={null} AddEventHandler('humalike:integration:ready', function(info) print(info.apiVersion, info.runtimeEpoch) end) ``` The event is local and emitted independently on server and client after each Humalike start. Register the providers for that side again when it fires. ## Next * [Implement the complete bridge example](/ai-npc/integrations/custom-bridge). * [Report player events](/ai-npc/integrations/world-events). * [Review the compact public reference](/ai-npc/reference). # Runtime control Source: https://docs.humalike.com/ai-npc/integrations/runtime-control Lease movement, animation, speech or perception of any active NPC for a bounded time. Any resource can take over selected AI domains of any active NPC (static, ambient, or a bound [external](/ai-npc/integrations/external-npcs) one). Humalike stops acting in those domains until the lease ends. The NPC's definition and entity are untouched. All exports on this page return one result table: `{ apiVersion = 1, ok = true, value = ... }` or `{ apiVersion = 1, ok = false, error = code }`. They must be called from a resource other than `humalike`, or they fail with `external_resource_required`. ## Acquire a lease ```lua theme={null} local result = exports.humalike:AcquireNpcControl(npcId, { domains = { 'movement', 'animation' }, ttlMs = 30000, reason = 'hostage_scenario', }) if not result.ok then return print(result.error) end local lease = result.value -- { id, npcId, kind, ownerResource, domains, reason, expiresInMs } exports.humalike:RenewNpcControl(lease.id, 30000) exports.humalike:ReleaseNpcControl(lease.id) ``` | Option | Description | | - | - | | `domains` | 1 to 4 of `movement`, `animation`, `speech`, `perception`, or `all` alone. | | `ttlMs` | 1,000 to 300,000. Default 30,000. | | `reason` | Optional string up to 128 characters, returned in the lease. | | Domain | Effect while held | | - | - | | `movement` | Movement actions are refused and Humalike stops pacing the ped. | | `animation` | Animation actions are refused and the current pose is forgotten. | | `speech` | The NPC does not speak. Voice targeting and labels show it as unavailable. | | `perception` | The NPC does not hear players and player events are not delivered to it. Server observations are still filed. | | `all` | All of the above. `aiEnabled` reports `false`. | Acquiring a lease cancels the NPC's in-progress action in that domain. A request that overlaps a held domain is refused as a whole. | Error | Meaning | | - | - | | `npc_not_active` | The NPC has no live entity. | | `invalid_domains`, `invalid_domain`, `all_domain_must_be_exclusive` | Bad `domains` list. | | `invalid_ttl`, `invalid_reason`, `invalid_options` | Argument shape. | | `domain_conflict` | A requested domain is already leased. | | `lease_not_found`, `not_owner` | Renew or release of an unknown or foreign lease. | A lease ends at its TTL, when its owner resource stops, when the NPC's entity disappears, or when an ambient NPC is re-assigned to another body. ## Runtime state ```lua theme={null} local state = exports.humalike:GetNpcRuntimeState(npcId).value local rows = exports.humalike:ListNpcRuntimeStates().value ``` | Field | Description | | - | - | | `kind` | `static`, `ambient` or `external`. | | `active` | Whether a live entity exists. | | `entity`, `networkId`, `routingBucket`, `modelHash` | Current entity. `entity` is a server handle; use `networkId` across boundaries. | | `aiEnabled` | `false` while every domain is leased. | | `controlledDomains` | Map of domain to `{ leaseId, ownerResource, expiresInMs }`. | | `entityOwner` | `humalike`, `external` or `despawned`. | | `bindingId`, `despawnId`, `entityOwnerResource` | Present when a resource owns the entity or the despawn. | `ListNpcRuntimeStates` returns every roster NPC, including unbound external ones, plus current ambient bodies, sorted by `npcId`, each with `name` and `model`. `GetNpcRuntimeState` fails with `npc_not_found` for an id not on the roster. ## Next * [Bind your own ped to a character](/ai-npc/integrations/external-npcs). * [Report facts about the NPC](/ai-npc/integrations/world-events#server-observations). * [Compact export reference](/ai-npc/reference#server-exports). # Report player events Source: https://docs.humalike.com/ai-npc/integrations/world-events Translate server roleplay events into observations understood by AI NPCs. Use the server export when gameplay happens outside Humalike but nearby NPCs should observe it: ```lua theme={null} local accepted = exports.humalike:ReportPlayerEvent(playerId, event) ``` It returns `true` after local validation and queueing. It returns `false` for an invalid player, an unloaded character, or a malformed event. ## Roleplay actions ```lua theme={null} exports.humalike:ReportPlayerEvent(source, { type = 'rp_action', kind = 'me', text = 'places the envelope on the table', }) ``` `kind` is `me` or `do`. Text is trimmed, must contain no control characters, and is limited to 1,000 Unicode characters. Report from the server handler after your `/me` or `/do` resource has accepted the command. Do not let a client submit narration directly. ## Identity document ```lua theme={null} exports.humalike:ReportPlayerEvent(source, { type = 'identity_document_shown', full_name = 'Alex Carter', last_name = 'Carter', sex = 'X', ssn = '123456', issued_at = '2026-01-15', }) ``` All five fields are required strings, trimmed and limited to 128 Unicode characters without control characters. ## Items ```lua theme={null} exports.humalike:ReportPlayerEvent(source, { type = 'item_dropped', item_name = 'water', quantity = 2, }) ``` Supported types are `item_dropped` and `item_picked_up`. `item_name` may contain letters, digits, `_`, `.`, and `-`, with a maximum of 100 characters. `quantity` must be a positive integer. Use the framework's internal item name. The NPC's natural-language description comes from the surrounding context and character knowledge. ## Simple state events These events contain only `type`: ```lua theme={null} exports.humalike:ReportPlayerEvent(source, { type = 'police_badge_shown' }) ``` * `police_badge_shown` * `ems_badge_shown` * `doj_badge_shown` * `hands_raised` * `hands_lowered` Humalike observes the `HandsUp` player state bag automatically. Only report the hands events yourself if the server uses another authoritative state mechanism. ## Server observations Player events are a fixed vocabulary. For facts only your server knows, declare your own observations on the actions provider (`Namespace` + `Observations`, see the [action descriptor](/ai-npc/integrations/provider-api#action-descriptor)) and report each occurrence: ```lua theme={null} local result = exports.humalike:ReportObservation(npcId, playerId, key, fields, options) exports.humalike:ReportObservation(npcId, source, 'item_given', { item = 'amulet', quantity = 1, }) ``` | Argument | Type | Description | | - | - | - | | `npcId` | string | A roster NPC (static, or external and bound) or an ambient body your server currently leases. | | `playerId` | number | The player the fact concerns. The NPC remembers it as that character's deed. | | `key` | string | A declared observation key, without the namespace (`item_given`, not `myserver:item_given`). | | `fields` | table | Every declared field, exactly typed, and nothing else. | | `options` | table, optional | `{ react = false }` files the fact without a spoken reaction. Default is `react = true`. | Field values are checked on every report: * `string` values are at most 64 characters with no control characters. * `integer` values are whole. * `integer` and `number` values are finite and at most 2^53 in magnitude. The resource renders the template declared for the NPC's language, falling back to English, then to the first declared language, and sends that line verbatim. Only the rendered line and the checked values leave the server. A rendered line is at most 912 characters; `RegisterProvider` refuses a template whose worst-case render would exceed that. ### Return value ```lua theme={null} { ok = true, value = { key = 'myserver:item_given', text = '...' } } { ok = false, error = 'unknown_observation' } ``` | Error | Meaning | | - | - | | `invalid_player` | `playerId` is not a connected player. | | `character_not_loaded` | The player provider reports no loaded character. | | `npc_not_found` | `npcId` is not a roster NPC or leased ambient body. | | `npc_not_bound` | The external NPC has no bound, spawned ped. | | `unknown_observation` | `key` is not declared on the actions provider. | | `invalid_fields` | `fields` is not a table. | | `invalid_field:` | A declared field is missing or has the wrong type or size. | | `unknown_field:` | `fields` contains an undeclared key. | | `invalid_options` | `options` is not a table or `react` is not a boolean. | | `text_too_long` | The rendered line exceeds 912 characters. | Declaration mistakes are reported by `RegisterProvider` itself. ### Behavior A reported observation is a server fact, so it differs from a player event: * It is not gated on earshot. * It is filed and, with `react = true`, spoken even while a script holds `perception` on the NPC. Hold `speech` to keep the NPC silent. * A repeat of the same key within about 2.5 seconds is filed but not spoken again. With `humalike_developer_tools 1`, report one by hand while wiring the real hook: ```text theme={null} /humalike_dev observe [field=value ...] ``` ## Delivery behavior The export confirms local acceptance, not that an NPC will answer. The event is delivered with the player's current session and world context. NPCs may observe it, remember it, react silently, or speak depending on character and conversation state. ## Next * [Declare server-defined actions](/ai-npc/integrations/custom-actions) gated on these observations. * [Build a custom bridge](/ai-npc/integrations/custom-bridge). * [Review provider contracts](/ai-npc/integrations/provider-api). * [Troubleshoot roleplay commands](/ai-npc/operations#me-or-do-is-ignored). # Operations and troubleshooting Source: https://docs.humalike.com/ai-npc/operations Check runtime health and resolve common installation, NPC, voice, and provider problems. Start with the status commands. They expose the supported operational state without requiring verbose logs. ## Status commands Run in the server console: ```text theme={null} humalike_status humalikenpc:status ``` `humalike_status` reports credential bootstrap and the `player`, `inventory`, `dispatch`, and `actions` provider domains. `humalikenpc:status` reports the selected player provider, roster size, and latest roster synchronization. Run `humalike_status` in the client F8 console to inspect the interaction provider; it also prints where the interaction setting comes from: `server` (set in server.cfg and received), `default` (the server's settings arrived and leave it at `auto`) or `local` (not received yet). Run `/voice` to inspect the local voice connection. `humalikenpc:reload` requests an immediate roster refresh. The server and client commands are ACE-restricted where server command permissions apply. ## Provider states | State | Meaning | Action | | - | - | - | | `selected` | One available provider is active. | None. | | `disabled` | The domain is configured as `none`. | Enable it only if the feature is required. | | `unavailable` | No provider matches the setting. | Start its dependency or fix the configured name. | | `ambiguous` | Top available providers have equal priority. | Select one explicitly. | | `degraded` | A selected provider callback failed. | Inspect that bridge or dependency. | ## Temporary debug logging ```cfg theme={null} set humalike_debug 1 ``` Restart the resource, reproduce one issue, collect the relevant lines, then set it back to `0`. Debug mode is intentionally noisy and should not remain enabled on a busy production server. Remove credentials and signed connection URLs before sharing logs. ## Resource does not become ready Check, in order: 1. `humalike_license_key` uses `set`, contains the current server license, and appears before `ensure humalike`. 2. The registered callback URL contains this FXServer's globally routable literal public IPv4 address. 3. Inbound TCP traffic reaches that port. 4. The server registration is enabled. 5. The FXServer build is current and OneSync is enabled. Rotate the license only if it is lost or exposed; rotation invalidates the old value immediately. ## NPC is missing * Confirm that the server status is ready and the roster count is non-zero. * Confirm that the NPC and server are enabled in the dashboard. * For static NPCs, verify model, coordinates, heading, routing bucket, and map geometry. * For ambient NPCs, verify that ambient policy is enabled and that players are standing in a zone with foot traffic (a mountain range or the open sea gets nobody). * Run `humalikenpc:reload` once instead of repeatedly restarting the server. ## NPC does not answer * Verify the player's microphone and Humalike PTT binding. * Use `/voice` and check that the intended NPC is not muted for that player. * Move close and face the NPC for a direct conversation. * Check the server's available [NPC turns](/npc-turns). * Confirm that the NPC has a voice and is enabled. An NPC can also choose not to speak based on its character and current conversation. Test multiple clear utterances before treating silence as a connection failure. ## `/me` or `/do` is ignored Built-in integration depends on the framework's supported roleplay event. A custom chat resource may display the command without publishing that event. In that case, call `ReportPlayerEvent` from the chat resource's server handler. See [Report player events](/ai-npc/integrations/world-events). ## Inventory action is unavailable Run `humalike_status` and check the `inventory` domain. `give_item` is advertised only while an inventory provider is selected. Resolve an unavailable or ambiguous provider, then allow Humalike to refresh its capabilities. ## NUI callback or asset errors after an update Mixed releases are the common cause. Stop Humalike, replace the entire resource directory with the contents of one `humalike.zip`, and start it again. Do not overlay releases or copy only the compiled web files. ## Reporting a bug Include the Humalike release version, FXServer artifact version, framework and provider status, exact reproduction steps, and sanitized logs. Report security issues through [security@humalike.ai](mailto:security@humalike.ai). ## Next * [Look up public commands and exports](/ai-npc/reference). * [Review security guidance](/ai-npc/security). * [Open the AI NPC dashboard](https://humalike.ai/ai-npc). # Quickstart Source: https://docs.humalike.com/ai-npc/quickstart Install Humalike and talk to your first AI NPC. This guide takes a clean game server from registration to its first AI NPC. ## 1. Register the server Open the [AI NPC dashboard](https://humalike.ai/ai-npc), choose **Register server**, and enter: * A name that identifies the server in your dashboard. * Its globally routable public IPv4 callback URL, for example `http://PUBLIC_IPV4:30120`. Use the public IPv4 address players use to reach the FXServer. Hostnames, IPv6, private addresses, loopback addresses, and reserved example addresses are not accepted. The URL must use `http`, contain an explicit unprivileged port (`1024`–`65535`), and have no path, query, or fragment. The dashboard shows the new server license once. Copy it immediately. You can rotate a lost or exposed license later. ## 2. Download the resource Get the latest `humalike.zip` release by following the [installation guide](/ai-npc/installation). The archive contains a ready-to-run `humalike` directory. Extract it into your server resources: ```text theme={null} resources/ └── humalike/ ├── fxmanifest.lua ├── integration/ ├── npc/ ├── voice/ └── world/ ``` ## 3. Configure FXServer Add the license immediately before the resource starts: ```cfg theme={null} set humalike_license_key "ak_secret_replace_me" ensure humalike ``` Use `set`, not `setr`, for `humalike_license_key`. A replicated convar would expose the license to connected clients. Do not configure voice URLs or voice secrets. Humalike provisions voice automatically from the server license. ## 4. Verify the connection Start the server and run this in its console: ```text theme={null} humalike_status ``` The resource should report installed runtime credentials and a selected player provider. Framework and inventory providers appear when their dependencies are running. ## 5. Create an NPC In the dashboard: 1. Open the registered server. 2. Select **Create NPC**. 3. Start with a **Static** NPC. 4. Choose its name, personality, language, voice, model, and coordinates. 5. Save and enable it. The NPC appears after the next roster refresh. You can also restart only the `humalike` resource while setting up a new server. ## 6. Talk Join the server, approach the NPC, hold your configured voice push-to-talk key, and speak. Humalike selects the NPC you are addressing from proximity, view direction, following state, and vehicle context. If the NPC appears, shows an active voice indicator, and answers, the core installation is complete. If any step fails, see [Operations and troubleshooting](/ai-npc/operations). ## Next * [Configure Humalike](/ai-npc/configuration) for your server. * [Choose a framework integration](/ai-npc/frameworks). * [Understand voice targeting](/ai-npc/voice). # Public reference Source: https://docs.humalike.com/ai-npc/reference Index of stable convars, commands, exports, events, providers, and actions. This page is a compact index. Follow the linked guides for callback contracts, examples, and operational guidance. ## Convars | Area | Public settings | | - | - | | Required | `humalike_license_key` | | Diagnostics | `humalike_debug` | | Providers | `humalike_player`, `humalike_inventory`, `humalike_dispatch`, `humalike_actions`, `humalike_interaction` | | Interface | `humalike_ui_language` | | Labels | `humalike_npc_labels_*` | | Medical gameplay | `humalike_npc_wounded_*`, `humalike_npc_death_chance`, `humalike_npc_revive_*`, `humalike_npc_mortuary_*`, `humalike_npc_treatment_distance`, `humalike_npc_deceased_retention` | | Jobs and dispatch | `humalike_npc_medic_jobs`, `humalike_npc_police_jobs`, `humalike_npc_medical_dispatch*` | | Wounds | `humalike_npc_wounds_enabled`, `humalike_npc_wound_*`, `humalike_npc_max_wounds` | | Poses | `humalike_npc_pose_abandon_*`, `humalike_npc_pose_threat_*` | | World tuning | `humalike_world_*` listed in [Configuration](/ai-npc/configuration#advanced-world-tuning) | Undocumented endpoint, runtime-secret, and authority convars are not a supported customer interface. ## Commands | Side | Command | Purpose | | - | - | - | | Server | `humalike_status` | Runtime and provider status. | | Client | `humalike_status` | Interaction provider status and whether the server's settings arrived. | | Server/player | `humalikenpc:status` | NPC roster status. | | Server/player | `humalikenpc:reload` | Request an immediate roster refresh. | | Client | `/voice` | Local voice connection status. | Developer-only ambient spawn tools are disabled by default. To use them on a non-production test server, set `humalike_developer_tools 1` and grant the ACE `humalike.developer_tools`. The command is: ```text theme={null} /humalike_dev ambient [npc_uuid] /humalike_dev observe [field=value ...] ``` Debug spawns are temporary and are not a replacement for dashboard-managed NPC placement. ## Server exports ```lua theme={null} exports.humalike:RegisterProvider(domain, descriptor) exports.humalike:UnregisterProvider(domain, name) exports.humalike:GetProviderStatus() exports.humalike:ReportPlayerEvent(playerId, event) exports.humalike:ReportObservation(npcId, playerId, key, fields, options) exports.humalike:SetNpcStock(npcId, stock) exports.humalike:PlaceOrder(npcId, playerId, lines) exports.humalike:BindNpcEntity(npcId, networkId, options) exports.humalike:UnbindNpcEntity(bindingId) exports.humalike:DespawnNpc(npcId) exports.humalike:RespawnNpc(npcId) exports.humalike:AcquireNpcControl(npcId, options) exports.humalike:RenewNpcControl(leaseId, ttlMs) exports.humalike:ReleaseNpcControl(leaseId) exports.humalike:GetNpcRuntimeState(npcId) exports.humalike:ListNpcRuntimeStates() ``` See [Provider API](/ai-npc/integrations/provider-api), [Report player events](/ai-npc/integrations/world-events), [Server-defined actions](/ai-npc/integrations/custom-actions) and [External NPCs](/ai-npc/integrations/external-npcs) and [Runtime control](/ai-npc/integrations/runtime-control). ## Client exports ```lua theme={null} exports.humalike:RegisterInteractionProvider(descriptor) exports.humalike:UnregisterInteractionProvider(name) exports.humalike:GetInteractionProviderStatus() exports.humalike:SetVoiceBusy(reason, active) ``` ## Events | Side | Event | Payload | | - | - | - | | Server and client | `humalike:integration:ready` | `{ apiVersion, runtimeEpoch }` | | Server | `humalike:npc:ready` | `{ apiVersion, generation }` | | Client | `humalike:voice:transmittingChanged` | `active: boolean` | ## Built-in provider names * Player: `standalone`, `esx`, `qbcore`, `qbox`. * Inventory: `esx`, `ox_inventory`, `qb_inventory`. * Interaction: `builtin`, `ox_target`, `qb_target`. ## Built-in action keys `wave`, `start_dancing`, `interrupt_animation`, `kneel`, `hands_up`, `punch`, `stand_up`, `follow_player`, `stop_following`, `hold_position`, `release_movement`, `enter_vehicle`, `enter_own_vehicle`, `exit_vehicle`, `walk_away`, and `run_away`. `give_item` is added when an inventory provider is available. The selected custom actions provider may advertise additional keys. ## Neutral player event types `rp_action`, `identity_document_shown`, `item_dropped`, `item_picked_up`, `police_badge_shown`, `ems_badge_shown`, `doj_badge_shown`, `hands_raised`, and `hands_lowered`. ## Next * [Read the complete provider contracts](/ai-npc/integrations/provider-api). * [See event payload examples](/ai-npc/integrations/world-events). * [Diagnose a running server](/ai-npc/operations). # Security and license Source: https://docs.humalike.com/ai-npc/security Protect server credentials and extend the resource safely. ## Protect the server license The Humalike server license is a secret. Configure it with server-only `set`: ```cfg theme={null} set humalike_license_key "ak_secret_replace_me" ``` Never use `setr`, commit the value, paste it into a client script, include it in a screenshot, or publish it in logs. Rotate it from the dashboard if exposed. Humalike derives short-lived runtime access from the license. Do not copy, persist, log, or manually configure those runtime values. ## Trust boundaries Client events and action parameters are untrusted. Custom providers must enforce permissions and validate player state, distance, item names, quantities, metadata, account amounts, and framework identifiers on the server. Use `ReportPlayerEvent` from an authoritative server handler. Do not expose a generic client event that lets a player report arbitrary identity, item, badge, or roleplay observations. ## Separate custom code Keep custom bridges outside the `humalike` directory. This prevents accidental credential disclosure, makes ownership clear, and permits complete verified resource updates. Register only the domains the bridge implements. Use specific provider names, minimal exports, and the least ACE permissions necessary. ## Source-available license Humalike is released under the PolyForm Shield License 1.0.0. You may use and modify it as part of a monetized game server. You may not use it to provide a product or service that competes with Humalike's AI NPC software or services. The repository's license is authoritative. ## Vulnerabilities Do not open a public issue for a suspected vulnerability. Use [security@humalike.ai](mailto:security@humalike.ai) and include the affected release, impact, and reproduction steps without real credentials. ## Next * [Install a verified release](/ai-npc/installation). * [Apply server-side validation in a bridge](/ai-npc/integrations/provider-api#action-descriptor). * [Review the public interface index](/ai-npc/reference). # Voice and direct targeting Source: https://docs.humalike.com/ai-npc/voice Understand proximity voice, vehicle audio, PTT, and NPC selection. Humalike NPC voice uses the WebRTC client included in the resource and is configured automatically from the server license. Mumble is not used to carry NPC audio, and server owners do not configure a voice endpoint or voice secrets. Player voice remains owned by the server's normal voice resource. When `pma-voice`, `saltychat` or `yaca-voice` is running, Humalike observes its radio and phone state so it does not transmit to NPCs during those conversations. The `pma-voice` adapter is verified in-game; the SaltyChat and yaca adapters follow those resources' published events and exports and have not been run against them yet. Any other voice or phone resource reports that itself from the client: ```lua theme={null} exports.humalike:SetVoiceBusy('phone_call', true) -- while the call lasts exports.humalike:SetVoiceBusy('phone_call', false) ``` Reasons are namespaced by the calling resource and dropped when it stops. ## Push to talk Players speak to AI NPCs using Humalike's push-to-talk binding. They can change the binding in game key settings. The HUD indicates whether Humalike is currently transmitting. A custom HUD can subscribe on the client: ```lua theme={null} AddEventHandler('humalike:voice:transmittingChanged', function(active) -- Update the server's voice indicator. end) ``` ## Which NPC hears the player The resource continuously maintains a small set of direct conversation targets so the HUD reflects the current routing before the player presses PTT. It prioritizes: 1. An NPC following that player. 2. NPCs sharing the player's vehicle. 3. A nearby NPC the player's ped and gameplay camera are directed toward. For an ordinary face-to-face conversation, the target must be close. If several NPCs qualify by proximity and view direction, the closest one is selected. Direct targets replace the ordinary unmuted audience for that utterance, which prevents a nearby ambient NPC and a farther static NPC from both responding. Following and same-vehicle relationships remain eligible beyond the normal face-to-face range. This keeps a companion audible and responsive while moving through a crowded area. ## Proximity and muted indicators Humalike limits how many NPC conversations are active around each player. The label shows when an NPC is not currently in that player's listening set. Direct target selection updates that state so the NPC the player is addressing is not presented as unavailable while other nearby NPCs are temporarily muted. Muting is per player. Two players in the same area can have different active NPCs without changing the NPC globally. ## Vehicles Players and NPCs in the same vehicle cabin remain voice-connected. An NPC that is following a player is prioritized for that player, including after entering a vehicle. Other players keep their own routing and priorities. ## Diagnose voice Run `/voice` in the game client to inspect the local connection. Also check: * the browser/NUI microphone permission; * the configured Humalike PTT key; * that the server license is valid; * that the registered callback still points at this FXServer; * that OneSync is enabled. Enable `humalike_debug` only while diagnosing a problem. Do not publish logs that contain connection URLs or credentials. ## Next * [Configure Humalike](/ai-npc/configuration). * [Review following and vehicle behavior](/ai-npc/gameplay#following-and-vehicles). * [Troubleshoot an NPC that does not answer](/ai-npc/operations#npc-does-not-answer). # NPC turns Source: https://docs.humalike.com/npc-turns How the AI NPC product is billed: turns, the free trial week, the two plans, auto-purchase, and expiry. The AI NPC product (game servers) is not billed in credits. It has its own currency: **turns**. Everything else on this page exists so your NPCs never stop talking mid-session. ## What a turn is **One delivered NPC reply is one turn.** If players heard it, it counts — a reply to something a player said, a spoken reaction to a world event, or a reply that was interrupted but had an audible start. One reply to a whole group is still one turn. What is **free**: interjections (the short quips an NPC drops while someone else is talking), replies that were never delivered, players talking to NPCs (listening costs nothing), and **world generation** — creating worlds, groups and personas, editing them, and the background world simulation are all unmetered. ## Plans — both start free | Plan | Price | Turns | | - | - | - | | \$79 | \$79/mo | 25,000 turns per month | | \$119 | \$119/mo | 50,000 turns per month | Every account starts with a **free trial week**, whichever plan you pick: choose a plan, add your card, and pay **\$0 today**. You get **1,000 trial turns** that last **one week**, and your plan's first charge happens after **7 days — or the moment the trial turns run out**, whichever comes first. From then on it's a normal monthly plan. One trial per account, ever: cancel during the trial and you pay nothing (any unused trial turns still expire at the end of the trial week), but subscribing again later starts — and bills — the plan immediately. Turns from every paid grant **accumulate**: a renewal adds the month's turns on top of what you still have. ## Auto-purchase Auto-purchase is **on by default** once a card is on file, because a busy server that runs dry mid-evening is worse than a charge you expected: * Run out of trial turns → your trial ends and your chosen plan starts (that's the trial's deal, and it happens whether or not auto-purchase is on). * Run out on the $79 plan → you're charged the $40 difference, granted the 25,000-turn difference, and moved to the $119 plan (the renewal date doesn't move; the next renewal bills $119). * Run out on the $119 plan → a one-time $50 top-up pack of **33,300 turns** is charged (never a second subscription), at most three times per cycle. You can turn auto-purchase off in the console ("auto-purchase for constant uptime"). With it off, your plan just renews monthly, and when the turns run out your NPCs will go quiet until you buy more (once muting goes live — see below). ## Expiry and downgrades Paid turns never expire at a renewal — **every paid grant lives for a full year** from the day it landed, including auto-purchased ones. The one exception is the 1,000-turn trial lot, which expires with the trial week. Downgrades take effect at the **next** billing cycle; what's paid is paid and is not refunded. Cancelling stops the renewals, and every paid turn you already have keeps its year. ## When the balance hits zero Balances are counted and purchases work today, but the automatic muting described here is **not yet enforced** — an exhausted balance doesn't silence anyone yet. We'll enable it with notice, so nothing changes under your servers unannounced. With auto-purchase off (or a card that declines), an exhausted balance will mute your servers' NPCs: they stay in the world, keep hearing and remembering, but stop speaking until turns arrive. The console's AI NPC page shows your live balance and warns before you get there.