Your First NPC
List your NPCs, connect to one, and stream a reply, in C++ and Blueprint.
ListNPCs finds an NPC, ConnectNPC opens a conversation. Everything after that is delegates.
List your NPCs
ListNPCs is async and Blueprint-callable, which is why it takes a callback delegate parameter rather than returning a value directly — even from C++, you bind to a UFUNCTION, not a lambda:
FMistscaleListNPCsResult Callback;
Callback.BindUFunction(this, FName("OnNPCsLoaded"));
Mistscale->ListNPCs(Callback);
UFUNCTION()
void AMyGameMode::OnNPCsLoaded(bool bSuccess, const TArray<FMistscaleNPCSummary>& Npcs, const FMistscaleError& Error)
{
if (!bSuccess || Npcs.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("No NPCs: %s"), *Error.Message);
return;
}
// Npcs[0].Id is ready to pass to ConnectNPC
}In Blueprint: drag off the Callback pin on List NPCs → Add Custom Event, and wire from there.
Connect and chat
UMistscaleNPCConnection* Connection = Mistscale->ConnectNPC(Npcs[0].Id);
Connection->OnChatChunk.AddDynamic(this, &AMyGameMode::OnChatChunk); // append streamed tokens
Connection->OnChatRevision.AddDynamic(this, &AMyGameMode::OnChatRevision); // grounding rewrote the reply — replace
Connection->OnChatMessage.AddDynamic(this, &AMyGameMode::OnChatMessage); // the turn is settled
Connection->OnOpened.AddDynamic(this, &AMyGameMode::OnConnectionOpened);
UFUNCTION()
void AMyGameMode::OnConnectionOpened()
{
Connection->SendChat(TEXT("Hello there!"));
}If you don't want to render a live stream, just bind OnChatMessage and ignore OnChatChunk / OnChatRevision entirely — it always fires once per turn with the final text, whether or not you consumed the intermediate chunks. In Blueprint, bind delegates via Assign On Chat Chunk / Assign On Chat Message on the connection.
The connection surface
SendChat(Message, SenderId = "")SendVoiceChunk(Data, bEnd = false, SenderId = "")SetSpatialContext(Location, TimeOfDay = "", Weather = "")GetEvolutionStatus() / GetQuotaStatus()Close()GetState()Delegates
OnOpened / OnClosed(Code, Reason, bExpected) / OnSdkError(Error)OnChatChunk(ChatId, Delta)OnChatRevision(ChatId, Text)OnChatMessage(ChatId, Text, bFinalizedByMetadata, Metadata)OnChatBlocked(ChatType, Reason, Limit, Used)OnReconnecting(Attempt, DelayMs)Configure, in which case two running game instances get two separate relationships with the same NPC. For a real game, pass your own stable player id via ConnectNPC(NpcId, PlayerId) so returning players are recognized across sessions and devices.UMistscaleSubsystem keeps a strong reference to every connection it creates for the lifetime of the game instance, so a returned UMistscaleNPCConnection* won't be garbage collected out from under you even if you don't keep your own reference.