I think you’re on the right lines, but, from memory, there’s a timing issue.
Rather than copy the original, then make it invisible, I spawn a new object from a template with an invisible appearance and the desired name / portrait. Then I use AssignCommand to make the new object start the conversation.
The subtle difference is that in the very brief time it takes for the action queue to open, the name and portrait seem to get initialised correctly.
For creatures, OnConversation can be scripted to do almost anything - by all means post the code snippet that’s not working, I’m sure we can help.
It was indeed a timing issue, I had to had 0.2s delay in order for the portrait to show up correctly.
Here is the code snippet if anyone is having running into the same issue
void DDIAG__StartDialog(object oPC, object oSpeakee, int bPrivate = FALSE, int bPlayHello = TRUE, string sScript = "") {
string sRealScript = sScript;
if (sRealScript == "") sRealScript = GetTag(oSpeakee) + "_ddiag"; // Search a script with _ddiag suffix is none is provided
object oSpeakeeCopy = CreateObject(OBJECT_TYPE_PLACEABLE, "placeable_invisi", GetLocation(oSpeakee)); // placeable with appearance type "invisible"
SetName(oSpeakeeCopy, GetName(oSpeakee));
SetPortraitResRef(oSpeakeeCopy, GetPortraitResRef(oSpeakee));
// These are just for my own dynamic dialog, but it can work with a usual static dialog
SetLocalString(oSpeakeeCopy, DDIAG__SCRIPT_HANDLER_VARNAME, sRealScript);
SetLocalObject(oSpeakeeCopy, DDIAG__PC_VARNAME, oPC);
SetLocalObject(oSpeakeeCopy, DDIAG__SPEAKEE_VARNAME, oSpeakee);
AssignCommand(oSpeakeeCopy, DelayCommand(0.2f, ActionStartConversation(oPC, "ddiag", bPrivate, bPlayHello))); // The 0.2s delay is necessary for Portrait and Name to initialise on oSpeakeeCopy
}