Page 1 of 1

Calling Method From A Scriptable Object in Conversation

Posted: Sun Mar 17, 2024 2:44 am
by DrewThomasArt
I'm integrating the system with Suriyun's MMORPG Kit, and I'm trying to get the Accept/Decline Quest UI to pop up after certain dialogue nodes by using sequencer commands.
I've seen posts on using "SendMessage(B,,MyGameObject)" and "SendMessage(B,,listener)", but I'm wondering how to make it find a scriptable object that's attached to a specific NPC (or even just call the SO from resources?)

MMORPG Kit has this dialogue component that you attach to the NPC,
NPC_Component.png
NPC_Component.png (42.5 KiB) Viewed 493 times
Which would pop up a window with the index of quests in the SO,
QuestList_SO.png
QuestList_SO.png (52.51 KiB) Viewed 493 times
And then clicking on a quest calls this "RenderQuestUI()" method. (From the "NpcDialog" component script above)

Code: Select all

protected virtual void RenderQuestUI(UINpcDialog uiNpcDialog, BasePlayerCharacterEntity characterEntity, List<UINpcDialogMenuAction> menuActions)
        {
            if (uiNpcDialog.onSwitchToQuestDialog != null)
                uiNpcDialog.onSwitchToQuestDialog.Invoke();

            if (uiNpcDialog.uiCharacterQuest != null)
            {
                if (quest != null)
                {
                    UINpcDialogMenuAction acceptMenuAction = new UINpcDialogMenuAction();
                    UINpcDialogMenuAction declineMenuAction = new UINpcDialogMenuAction();
                    UINpcDialogMenuAction abandonMenuAction = new UINpcDialogMenuAction();
                    UINpcDialogMenuAction completeMenuAction = new UINpcDialogMenuAction();

                    acceptMenuAction.title = uiNpcDialog.MessageQuestAccept;
                    acceptMenuAction.icon = uiNpcDialog.questAcceptIcon;
                    acceptMenuAction.menuIndex = QUEST_ACCEPT_MENU_INDEX;

                    declineMenuAction.title = uiNpcDialog.MessageQuestDecline;
                    declineMenuAction.icon = uiNpcDialog.questDeclineIcon;
                    declineMenuAction.menuIndex = QUEST_DECLINE_MENU_INDEX;

                    abandonMenuAction.title = uiNpcDialog.MessageQuestAbandon;
                    abandonMenuAction.icon = uiNpcDialog.questAbandonIcon;
                    abandonMenuAction.menuIndex = QUEST_ABANDON_MENU_INDEX;

                    completeMenuAction.title = uiNpcDialog.MessageQuestComplete;
                    completeMenuAction.icon = uiNpcDialog.questCompleteIcon;
                    completeMenuAction.menuIndex = QUEST_COMPLETE_MENU_INDEX;

                    CharacterQuest characterQuest;
                    int index = characterEntity.IndexOfQuest(quest.DataId);
                    if (index >= 0)
                    {
                        characterQuest = characterEntity.Quests[index];
                        if (!characterQuest.isComplete)
                        {
                            if (!characterQuest.IsAllTasksDoneAndIsCompletingTarget(characterEntity, characterEntity.GetTargetEntity() as NpcEntity))
                                menuActions.Add(abandonMenuAction);
                            else
                                menuActions.Add(completeMenuAction);
                        }
                        else if (characterEntity.Quests[index].GetQuest().CanReceiveQuest(characterEntity))
                        {
                            menuActions.Add(acceptMenuAction);
                            menuActions.Add(declineMenuAction);
                        }
                    }
                    else
                    {
                        characterQuest = CharacterQuest.Create(quest);
                        menuActions.Add(acceptMenuAction);
                        menuActions.Add(declineMenuAction);
                    }
                    uiNpcDialog.uiCharacterQuest.Setup(characterQuest, characterEntity, index);
                }
                uiNpcDialog.uiCharacterQuest.Show();
            }
        }
So basically I'm just trying to figure out how to access a SO of quests and call the above method on that SO from the sequencer
With that, I can do it for the shop, storage, etc- and seems like the simplest way of integrating. Any ideas?

Edit: Better yet, call this method that calls the pop up window depending on what dialogue type the SO is (quest, shop, etc. Also from the NPCDialog component script)

Code: Select all

public override async UniTask RenderUI(UINpcDialog uiNpcDialog)
        {
            BasePlayerCharacterEntity characterEntity = GameInstance.PlayingCharacterEntity;

            if (type != NpcDialogType.Shop && uiNpcDialog.uiSellItemRoot != null)
                uiNpcDialog.uiSellItemRoot.SetActive(false);

            if (type != NpcDialogType.Shop && uiNpcDialog.uiSellItemDialog != null)
                uiNpcDialog.uiSellItemDialog.Hide();

            if (type != NpcDialogType.Quest && uiNpcDialog.uiCharacterQuest != null)
                uiNpcDialog.uiCharacterQuest.Hide();

            if (type != NpcDialogType.CraftItem && uiNpcDialog.uiCraftItem != null)
                uiNpcDialog.uiCraftItem.Hide();

            List<UINpcDialogMenuAction> menuActions = new List<UINpcDialogMenuAction>();
            switch (type)
            {
                case NpcDialogType.Normal:
                    await RenderNormalUI(uiNpcDialog, characterEntity, menuActions);
                    break;
                case NpcDialogType.Quest:
                    RenderQuestUI(uiNpcDialog, characterEntity, menuActions);
                    break;
                case NpcDialogType.Shop:
                    RenderShopUI(uiNpcDialog);
                    break;
                case NpcDialogType.CraftItem:
                    RenderCraftUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.SaveRespawnPoint:
                    RenderSaveRespawnPointUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.Warp:
                    RenderWarpUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.RefineItem:
                    RenderRefineItemUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.PlayerStorage:
                    RenderPlayerStorageUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.GuildStorage:
                    RenderGuildStorageUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.DismantleItem:
                    RenderDismantleItemUI(uiNpcDialog, menuActions);
                    break;
                case NpcDialogType.RepairItem:
                    RenderRepairItemUI(uiNpcDialog, menuActions);
                    break;
            }
            RenderMenuUI(uiNpcDialog, menuActions);
        }

Re: Calling Method From A Scriptable Object in Conversation

Posted: Mon Mar 18, 2024 3:36 pm
by Tony Li
Hi,

I recommend writing a custom sequencer command. You can use the properties 'speaker' and 'listener' to refer to the GameObjects associated with the dialogue entry's actor (speaker) and conversant (listener) so you don't need to reference anything by GameObject name. Then you can get GetComponent<NpcEntity>() on, say, the 'speaker' to get its component and its associated SO.

Re: Calling Method From A Scriptable Object in Conversation

Posted: Tue Mar 19, 2024 12:24 am
by DrewThomasArt
Tony Li wrote: Mon Mar 18, 2024 3:36 pm Hi,

I recommend writing a custom sequencer command. You can use the properties 'speaker' and 'listener' to refer to the GameObjects associated with the dialogue entry's actor (speaker) and conversant (listener) so you don't need to reference anything by GameObject name. Then you can get GetComponent<NpcEntity>() on, say, the 'speaker' to get its component and its associated SO.
Thank you, I'm trying this way, but I think I'm putting in the argument for the sequencer method incorrectly. Does this require the GetSubject() function somehow? My script:

Code: Select all

 public class SequencerCommandRenderUI : SequencerCommand
    { 
       public UINpcDialog uiNpcDialog;
        public void Awake()
        {
            speaker.GetComponent<NpcEntity>().StartDialog.RenderUI(uiNpcDialog);
           Debug.Log("Called RenderUI");
           Stop();
        }
}
The method I'm calling, with UINPCDialog being the prefab dialogue UI:

Code: Select all

public abstract UniTask RenderUI(UINpcDialog uiNpcDialog);
        /// <summary>
        /// This will be called to un-render previous dialog
        /// </summary>
        /// <param name="uiNpcDialog"></param>

Code: Select all

public override async UniTask RenderUI(UINpcDialog uiNpcDialog)
        { //stuff
        }
The error:
Screenshot 2024-03-18 222554.png
Screenshot 2024-03-18 222554.png (19.96 KiB) Viewed 438 times

Re: Calling Method From A Scriptable Object in Conversation

Posted: Tue Mar 19, 2024 3:07 am
by Tony Li
Hi,

Your code is fine as long as the correct GameObject is being used as the speaker. Add a Debug.Log to show the value of the speaker property. It's probably the wrong value. See Character GameObject Assignments.

Re: Calling Method From A Scriptable Object in Conversation

Posted: Tue Mar 19, 2024 3:00 pm
by DrewThomasArt
Tony Li wrote: Tue Mar 19, 2024 3:07 am Hi,

Your code is fine as long as the correct GameObject is being used as the speaker. Add a Debug.Log to show the value of the speaker property. It's probably the wrong value. See Character GameObject Assignments.
I also tried using "GameObject.Find" to get the exact NPC instead of using speaker, with the same error, so I don't know
I think the issue is coming from the MMORPG Kit code, I'll try his Discord because I shouldn't bother you with that if it's the case

Re: Calling Method From A Scriptable Object in Conversation

Posted: Tue Mar 19, 2024 5:34 pm
by Tony Li
Sounds good. But it's probably just as likely on your script's interface with the Dialogue System. I recommend adding that Debug.Log to confirm which GameObject is being used as the speaker.