OnNpcConversationRespond
OnNpcConversationRespond is called on the server when a player presses a response option in an NPC conversation, just before the game evaluates that response's conditions and runs its actions.
object OnNpcConversationRespond(NPCTalking nPCTalking, BasePlayer player, ConversationData conversationFor, ConversationData.ResponseNode responseNode)Parameters
| Parameter | Type | Description |
|---|---|---|
nPCTalking | NPCTalking | The NPC entity that owns the conversation UI (typically a shopkeeper or mission NPC). Use it to read position, net ID, or nearby players. |
player | BasePlayer | The BasePlayer who pressed a response button in the NPC conversation. Null-check it before sending chat or granting items. |
conversationFor | ConversationData | The ConversationData the dialogue belongs to. Useful for identifying the conversation in general terms. |
responseNode | ConversationData.ResponseNode | The specific ConversationData.ResponseNode the player selected, including its index among the NPC's responses. Use it to branch on which option was clicked. |
Return value
Returning a non-null object short-circuits Server_ResponsePressed, so the response's conditions are not evaluated and no mission, item, or follow-up action is performed. Returning null lets the game continue with normal processing of the selected response.
How it works
This hook fires inside NPCTalking.Server_ResponsePressed, after the game resolves which response node the player chose (abstractSpeechNodeData.responses[num2]) and assigns it to responseNode. It runs before the response's conditions array is checked and before any mission assignment, item give, or follow-up speech the response would trigger. That timing makes it a gate: you see the player, the NPC, the conversation, and the exact response node, and you can stop everything downstream.
nPCTalking is the NPC entity, so you can reject responses when the player is too far away or the NPC is busy. player is the responding player. conversationFor identifies the conversation, and responseNode identifies the specific option, which is what you usually branch on (for example by its title text or position in the response list).
Because the game passes an NPCTalking argument, declaring the parameter as a broader type such as BaseEntity still matches, but you then cannot call conversation-specific members without a cast. Oxide only calls your method when every argument fits the declared parameter type, so a more specific parameter type is fine here.
Example plugin: Block NPC shop responses while the player is in combat
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Npc Conversation Guard", "RustPluginGenerator", "1.0.0")]
[Description("Blocks NPC conversation responses while the player is in combat or low on health.")]
public class NpcConversationGuard : RustPlugin
{
private const string PermBypass = "npcconversationguard.bypass";
private PluginConfig config;
private readonly Dictionary<ulong, float> lastDamage = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Combat block seconds")]
public float CombatSeconds = 10f;
[JsonProperty("Minimum health to respond")]
public float MinHealth = 0f;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Blocked"] = "You cannot use this NPC while in combat. Wait {0} more seconds.",
["LowHealth"] = "Your health is too low to respond to this NPC."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermBypass, this);
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || info == null) return;
var player = entity as BasePlayer;
if (player == null || player.IsNpc) return;
lastDamage[player.userID] = UnityEngine.Time.realtimeSinceStartup;
}
private object OnNpcConversationRespond(NPCTalking nPCTalking, BasePlayer player, ConversationData conversationFor, ConversationData.ResponseNode responseNode)
{
if (player == null || player.IsNpc) return null;
if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;
float remaining;
if (lastDamage.TryGetValue(player.userID, out var stamp))
{
remaining = config.CombatSeconds - (UnityEngine.Time.realtimeSinceStartup - stamp);
if (remaining > 0f)
{
player.ChatMessage(Msg("Blocked", player.UserIDString, Mathf.CeilToInt(remaining)));
return true;
}
}
if (config.MinHealth > 0f && player.health < config.MinHealth)
{
player.ChatMessage(Msg("LowHealth", player.UserIDString));
return true;
}
return null;
}
private void Unload()
{
lastDamage.Clear();
}
}
}The plugin caches the player's last damage time by listening to OnEntityTakeDamage for BasePlayer victims. In OnNpcConversationRespond, it checks the responder: if they took damage within the configured window (or have less than the configured minimum health), the response is cancelled and a language message is sent telling them to wait. Because the hook returns a non-null value, the game never evaluates the response's conditions or grants the shop item. config.json exposes the combat window in seconds and the minimum health threshold, both loaded through the standard Oxide configuration override.
Common use cases
Block conversations during combat
Deny NPC responses when the player was recently hurt, so shops and missions cannot be used as a safe mid-fight.
Restrict access by location
Cancel responses if the player moved away from the NPC's position, preventing players from completing remote dialogue triggers.
Gate special options on permissions
Inspect the response node and block it unless the player holds a specific permission, effectively whitelisting premium mission lines.
Pitfalls
Cancelling without feedback
A non-null return silently discards the click; without a chat or lang message the player just sees nothing happen and may spam the option.
Assuming the node contents
Response nodes vary between conversations; do not depend on a specific index being present in every
ConversationData, always bounds-checkresponsesbefore branching.Heavy work inside the hook
This runs on the main thread per click; keep it to simple checks and avoid spawning entities or running queries that could stall the NPC dialogue.
Build a plugin with OnNpcConversationRespond
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnNpcConversationRespond
Generate