Multiplayer Team Quest

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
delgelato
Posts: 8
Joined: Sat Aug 11, 2018 11:15 am

Multiplayer Team Quest

Post by delgelato »

Hi,

This should be the last major integration that I need to do. I'm currently working on it but got some dilemma here n there.

So players can have team (using Photon Teams). Currently if there's a late joining player, and they just chose a team, they'll get a sync of all the quest marked "Team Quest" from the older player.
Now there's a "Team Quest" field in the Quest element. Setting 1 of them to TeamQuest is easy (SetTableField).
But is there a DialogueLua like "Quest[] GetAllElementFromTable(tableName)" so I can use the result to sync every Quest that's marked Team Quest? Or at least "GetAllKeyFromTable" so I can check each Key if it exist then check the value.
If not I'll have to keep track of all quest marked TeamQuest in my own class.

And then about the syncing team quest itself. This is more of a "asking if I'm on the right track" kinda question.
Say A and B are in the same team. The TeamQuest they got is "kill 5 orc". Everytime an orc is killed by either, they'll +1 "OrcKilled" in local and sync the variable to other players with the same team.

Then the next part depends on how the "SetQuestState = success" is triggered.
If after 5 Orc killed then you gotta talk to the NPC, and the conditioned dialogue is the one to set State = success AND give the reward, then I should only sync the variable and let each player collect reward and "success" up the quest themselves in local.
If the success triggered is checked from each orcs killed, then I should also set state = success each time the variable value is received by the other players. The problem with this is I'm only sending variable update, I don't know which quest that variable is directed to. Again, if there's a way to GetAllQuestFromTable and check each if any of them has Orc Killed, then I can trigger the state = success on the corresponding quest.
And before you can guide me on this one, of course, let me know if I'm doing this correctly.
I'm not making an MMO, the quest assigning and updating doesn't have to be authoritative. In fact, later each player will have their own set of quest on top of some Team Quest, so it's a multiplayer but the quest part is more "single player" style.

Cheers
delgelato
Posts: 8
Joined: Sat Aug 11, 2018 11:15 am

Re: Multiplayer Team Quest

Post by delgelato »

Ok an update. I found that there's .items and .actor fields from DialogueManager.masterDatabase, which is used in the dropdown things used by QuestPicker. So I think I'll manage with that.

But what is masterDatabase? Is it different than initialDatabase that's set in DialogueSystemController? What if there's multiple database object? I just can't find the reference between those 2...

Cheers
User avatar
Tony Li
Posts: 21721
Joined: Thu Jul 18, 2013 1:27 pm

Re: Multiplayer Team Quest

Post by Tony Li »

Hi,

You may also want to use the QuestLog class. It's a static class, so all methods are used like:

Code: Select all

string[] activeQuests = QuestLog.GetAllQuests(QuestState.Active);
foreach (string questName in activeQuests)
 {
     if (DialogueLua.GetQuestField(questName, "TeamQuest").AsBool)
     {
         Debug.Log("Team quest: " + questName);
     }
 }
It's built on top of DialogueLua and just provides simpler, easier to read functions that are quest system-specific.

A dialogue database is an asset. It's actually a ScriptableObject. But it's generally easiest to think of it like an asset, which is read-only.

At runtime, the Dialogue System creates a new dialogue database in memory, DialogueManager.masterDatabase, and loads the Dialogue Manager's Initial Database into it. If you use the Extra Databases component or DialogueManager.AddDatabase() method, it will also load additional databases into masterDatabase. So masterDatabase represents the sum of all loaded databases. Think of its content as read-only.

Also at runtime, the Dialogue System loads the masterDatabase's actor, quest/item, location, and variable tables into the Lua environment. It's in the Lua environment where you can change values -- for example, using DialogueLua.SetVariable() or DialogueLua.SetActorField().

It's also possible to create quests at runtime by adding them to the Lua representation. The QuestLog class works with the Lua representation, as does the save system. If you're not creating new quests at runtime, you can read static values from DialogueManager.masterDatabase if it makes your code simpler. Just remember to read dynamic values, such as quest states, from the Lua representation.

As for multiplayer, you may want to look at LuaNetworkCommands.cs. It's written for UNET, but you could do something similar for PUN.
delgelato
Posts: 8
Joined: Sat Aug 11, 2018 11:15 am

Re: Multiplayer Team Quest

Post by delgelato »

Alright, I think I get the hang of what's read only and the Lua version that's dynamic.

Regarding the orc killed. I have my own method when a monster is killed. Currently I'm thinking of having a variable for every [monster]Killed, [monster]KilledBy. But then again, since I'm not using DS's ItemDB (Item count is directly checked in inventory), might put all monster as Item, with fields such as Killed, KilledBy, etc.
Either should work..

What I'm wondering is how to define more DST(DialogueSystemTrigger) on different events.
Currently when I pick item, I handle it accordingly considering the authoritative pickup. The item has Usable and a(some) DST that can trigger additional stuff like starts a quest. In my old (messy) quest system, quest can be triggered in various ways like pick/drop item, kill/killed by monster, but those triggers are defined in the quest entry, whereas DS lets the quest entry simple, and let various ways to trigger that quest to active.

The DST I attached to the Item is only there for the context of picking it up. But where would I attach another DST for the context of consuming/equipping the item?
I have a scriptableobject database for my items, I thought of adding a DST in the Item class, so I'll have:
OnPicked Condition
OnPicked ActionLua
OnUse/Consume Condition
OnUse/Consume ActionLua
etc.

And same with monsters.

Actually I might just be trying to insert Lua logic data everywhere, not just to set quest state or DS variables. Maybe even trigger certain barks when the player gets damaged by certain monster. Or barks/screen effect when drinking poison. I hope this is fine...

edit: forgot to ask. How to add variable/item at runtime? I can only find QuestLog.AddQuest..
edit: Add to the Lua Dynamic. I can find DialogueManager.MasterDatabase.variables.Add but isn't that static? Will it immediately be reflected in the Lua part if I do this?
And is this even recommended? Say in 1 of my Condition is Variable["ApplePicked"] but the variable is not even created yet. Should I register a function called CheckOrFalse (return false if not exist). Similarly if there is a way to add variable, I'll have to check if it already exist
User avatar
Tony Li
Posts: 21721
Joined: Thu Jul 18, 2013 1:27 pm

Re: Multiplayer Team Quest

Post by Tony Li »

delgelato wrote: Mon Aug 13, 2018 12:36 pmRegarding the orc killed. I have my own method when a monster is killed. Currently I'm thinking of having a variable for every [monster]Killed, [monster]KilledBy. But then again, since I'm not using DS's ItemDB (Item count is directly checked in inventory), might put all monster as Item, with fields such as Killed, KilledBy, etc.
Either should work..
Another alternative (and it's just your preference) is to add a custom field to the quest. So the "Kill 5 Rats" quest could have a custom Number field "numRatsKilled".

To show this as a quest entry in the quest tracker HUD, you'd want to set the quest entry text to something like:

Code: Select all

[lua(Quest["Kill_5_Rats"].numRatsKilled)] / 5 Killed
delgelato wrote: Mon Aug 13, 2018 12:36 pmWhat I'm wondering is how to define more DST(DialogueSystemTrigger) on different events.
You could add child GameObjects with DSTs set to OnUse, and manually call their OnUse() methods when appropriate. Make sure these children do not have Usable components; this prevents the players from selecting and using them on their own.

Another approach, similar to what you described, would be to use your own script(s). For example:

Code: Select all

public ConsumableItem : MyScriptableItemType
{
    public Condition consumeCondition; // Player can only consume if this condition is true.
    [LuaScriptWizard] public string consumeLuaAction; // Run this when consuming item.
    
    [LuaScriptWizard] public string dropLuaAction; // Run this when dropping item.
    
    public void TryConsume(Transform consumer)
    {
        if (consumeCondition.IsTrue(consume))
        {
            Lua.Run(consumeLuaAction);
        }
    }
}
The code above uses the [LuaScriptWizard] custom Dialogue System attribute. There are a bunch of attributes that make the inspector nicer for things like variable name popups and conversation title popups.
delgelato wrote: Mon Aug 13, 2018 12:36 pmActually I might just be trying to insert Lua logic data everywhere, not just to set quest state or DS variables. Maybe even trigger certain barks when the player gets damaged by certain monster. Or barks/screen effect when drinking poison. I hope this is fine...
Sure, lots of teams do this. In fact, at least one team did something clever and put Lua code in a quest field. Their quests looked a bit like this:
  • Quest: Kill 5 Rats
  • Description: Bob the Innkeeper offered you 20 gold to kill 5 rats.
  • RatsKilled (custom field): (Number)
  • Entry 1: [lua(Quest["Kill_5_Rats"].RatsKilled)] / 5 Killed
  • CompletionCondition (custom field):

    Code: Select all

    Quest["Kill_5_Rats"].RatsKilled >= 5
  • RewardScript (custom field):

    Code: Select all

    ShowAlert("Completed: Kill 5 Rats Quest");
    GiveGoldToPlayer(20)
To check if the quest is complete, they grab the quest's CompletionCondition field and check it with Lua.IsTrue().

When the quest is complete, they grab the quest's RewardScript field and run it with Lua.Run(). (They registered a custom Lua function named GiveGoldToPlayer.)

This enabled their level designers to design all of the quest logic without having to touch any actual C# code.
delgelato wrote: Mon Aug 13, 2018 12:36 pmedit: forgot to ask. How to add variable/item at runtime? I can only find QuestLog.AddQuest.
Just set it. Lua's very easygoing like that. The code below will add a new Lua variable named "SomeNewVariable" with a value of 42:

Code: Select all

DialogueLua.SetVariable("SomeNewVariable", 42);
delgelato wrote: Mon Aug 13, 2018 12:36 pmedit: Add to the Lua Dynamic. I can find DialogueManager.MasterDatabase.variables.Add but isn't that static? Will it immediately be reflected in the Lua part if I do this?
No, it won't be immediately reflected. You'd have to remove the database from the Dialogue Manager and re-add it, using DialogueManager.RemoveDatabase() and then DialogueManager.AddDatabase(). But since you can just add variables on the fly as described above, there's no need to go through that.
delgelato wrote: Mon Aug 13, 2018 12:36 pmAnd is this even recommended? Say in 1 of my Condition is Variable["ApplePicked"] but the variable is not even created yet. Should I register a function called CheckOrFalse (return false if not exist). Similarly if there is a way to add variable, I'll have to check if it already exist
If a variable doesn't exist, it will return "nil", which is Lua's equivalent to C#'s null.

I'll often use Boolean variables as flags, such as this when talking to an NPC named Adam:

Code: Select all

DialogueLua.SetVariable("TalkedToAdam", true);
Later on, I might have a condition that checks if the player has not talked to Adam:
  • Dialogue Text: "Hi! We haven't met yet. I'm Adam."
    Conditions:

    Code: Select all

    Variable["TalkedToAdam"] ~= true
I used the condition "~= true" because Variable["TalkedToAdam"] could be false or nil (or true).

The only downside to not defining the variable in your dialogue database ahead of time is that it's not available for selection in the inspector's Lua dropdown menus.
Post Reply