OnNpcConversationEnded
OnNpcConversationEnded is called on the server when a player finishes talking to an NPCTalking NPC, just before the game removes that player from the NPC's conversingPlayers list.
void OnNpcConversationEnded(NPCTalking nPCTalking, BasePlayer player)Parameters
| Parameter | Type | Description |
|---|---|---|
nPCTalking | NPCTalking | The NPC whose conversation is ending. It is the NPCTalking component that ran Server_OnConversationEnded, so you can read its entity position, net ID or flags. |
player | BasePlayer | The player leaving the conversation, or null when the method is called without a valid player. Always null-check before using it. |
Return value
The hook is called with the result ignored, so returning null, true or any value has no effect on the game. The original method continues and the player is removed from the NPC's conversingPlayers list as usual.
How it works
The hook fires inside NPCTalking.Server_OnConversationEnded, which the game runs when a conversation session with an NPC of type NPCTalking (for example mission or vendor-style dialogue NPCs) is closing. The call happens before the original method bodies out its cleanup, so at the moment your hook runs the player is still listed in conversingPlayers. Both parameters are passed straight from the method: the NPC component itself and the BasePlayer that ended the conversation.
Use the hook to react to conversation ends: close custom UI you opened for that player, hand out a reward once per NPC, log dialogue completion, or start a follow-up action. The player argument can be null in theory, so guard it. Declaring a more specific type for the second parameter will not narrow anything useful here since only BasePlayer is ever passed; making the first parameter a different type than NPCTalking would make Oxide skip calls entirely.
Because the return value is ignored, this hook cannot block or alter the conversation. It only observes. Keep the handler cheap: it can fire for every player interacting with many NPCs on a busy server.
Example plugin: Reward players the first time they finish an NPC conversation
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Npc Conversation Reward", "RustPluginGenerator", "1.0.0")]
[Description("Gives a configurable reward the first time a player finishes a conversation with a given NPC")]
public class NpcConversationReward : RustPlugin
{
private const string PermUse = "npcconversationreward.reward";
private class PluginConfig
{
[JsonProperty("Reward item shortname")]
public string ItemShortname = "scrap";
[JsonProperty("Reward amount")]
public int Amount = 25;
[JsonProperty("Reward only for permission holders")]
public bool PermissionOnly = false;
}
private class StoredData
{
public Dictionary<string, List<ulong>> Completed = new Dictionary<string, List<ulong>>();
}
private PluginConfig config;
private StoredData data;
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>
{
["Reward"] = "You received {0} x {1} for finishing the conversation.",
["NoItem"] = "Reward item '{0}' is invalid, contact an admin."
}, 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(PermUse, this);
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
}
private void Unload()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data);
}
private void OnNpcConversationEnded(NPCTalking nPCTalking, BasePlayer player)
{
if (nPCTalking == null || player == null || nPCTalking.IsDestroyed) return;
if (config.PermissionOnly && !permission.UserHasPermission(player.UserIDString, PermUse)) return;
var npcId = nPCTalking.net != null ? nPCTalking.net.ID.Value : 0UL;
var key = npcId.ToString();
List<ulong> players;
if (!data.Completed.TryGetValue(key, out players))
{
players = new List<ulong>();
data.Completed[key] = players;
}
if (players.Contains(player.userID.Get())) return;
var item = ItemManager.CreateByName(config.ItemShortname, Mathf.Max(1, config.Amount));
if (item == null)
{
PrintWarning(string.Format("Invalid reward item shortname: {0}", config.ItemShortname));
return;
}
player.GiveItem(item);
players.Add(player.userID.Get());
player.ChatMessage(Msg("Reward", player.UserIDString, item.amount, item.info.displayName.english));
}
}
}The plugin listens on OnNpcConversationEnded and, for each completed conversation, records a pair of identifiers (player user ID and NPC net ID) in a saved data file so the same player cannot farm the same NPC repeatedly. On a first completion it grants a configurable item and condition through ItemManager.CreateByName, warns in chat through the message table, and marks the data dirty. Init registers the npcconversationreward.reward permission, which limits rewards to selected players. All state lives in oxide/data/NpcConversationReward.json via Interface.Oxide.DataFileSystem, and Unload saves it. Null checks cover a missing player, a destroyed NPC and an invalid shortname in config.
Common use cases
One-time dialogue rewards
Give an item, a currency through a referenced plugin or a permission when a player finishes talking to a specific NPC for the first time.
Close custom dialogue UI
If you draw your own CUI panels around NPC interaction, destroy the panel for that player here so it does not stay on screen after the conversation closes.
Track NPC dialogue progress
Append conversation completions to a data file to build quest-style progression, then unlock later NPCs or commands based on the count.
Pitfalls
The return value does nothing
Returning false or any other value will not cancel the conversation end or keep the player in the NPC's list. Use
OnNpcConversationStartor the respond hooks if you need to influence dialogue.The player can be null
The original method checks
player != nullafter the hook call, so your code must do the same before touchingUserIDString, inventory or network state.Fires per player and per NPC
On a populated server with many NPC interactions this hook can run often. Do the minimum work in the handler, save data on a timer or in Unload instead of writing to disk every call.
Build a plugin with OnNpcConversationEnded
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 OnNpcConversationEnded
Generate