How would one go about linking DialogueTriggers and predicates together?

I’m currently stumped. I have a quest that works like this: A guard has a question, and I have to ask the general that question for him. While asking the question, I have 3 routes on how I want to approach it. A good, bad, and neutral route. The neutral route includes the general giving me a note to give to the guard. The other two are reliant on the player’s dialogue decisions. After I return to the guard, what I tell him at that point is reliant on what I had said to the general.

I had a working system where I just added a function called ChangeCurrentDialogue() to the DialogueTrigger script and it worked great for that, because I could have multiple different dialogues on each character for what ends up happening, but it got to be a lot of DialogueTrigger components on each character, so I figured these predicates would be the solution to that problem. I have not been able to figure out if there’s a way on how I could somehow link these triggers and predicates together, and just have one big dialogue with different responses to the one node, as opposed to having a bunch of different dialogues for each possible outcome. How could this be done, exactly? And would this even be an ideal thing to do, or is there a better way?


Screenshot 2022-12-10 151456
I just simply added a serializefield to the dialogueTriggerHolder, and set it to be the general’s GameObject:

We have two different places in each DialogueNode where we place a Trigger… when we Enter the node and when we Exit the node…

If we have a node that will do one of three different actions depending on which Player choice is involved, for example, you could put the triggers on the Enter trigger for each of the player choice nodes.

Thanks for your response; I already have that in place. I guess I should have mentioned that as well. In fact, I feel like I’m pretty well accustomed to the dialogue trigger system. My issue is that when I tried Debug.Logging the returns in the Evaluate bool using the AttachPredicateToTrigger() method, it returns both Good and Bad which I suppose makes sense, since both of them are listed within the switch statement. But I really only want one to return based on which choice we make(which trigger we had set in the dialogue triggers). It just doesn’t seem to be working. Am I on the right track with this, or should I try a different approach? Thanks!

Clarification time… probably should have started here:

It sounds like you’re looking for is a way to track the results of previous interactions… Possibly not related to this dialogue…

I’ll use a popular 90’s video game as an example: In the game Final Fantasy VII, there is a section of the game where the lead character (Cloud) goes on a “date” with one of four characters. Which character goes on the date is entirely dependent on actions taken up to that point. (See this wiki for more info)

The system works by having an affinity score with each of the characters. Positive interactions add to that affinity, negative actions reduce that affinity.

A similar mechanic is at work in the popular game World of Warcraft, where you have a reputation with all of the races of your Faction, and reputation with side factions/civilizations you encounter with the game. Good actions (usually completing quests, but sometimes killing specific enemies) increases your reputation with a related faction. Bad actions (usually killing specific enemie) decreases your reputation with a related faction. In Classic WoW, there were even two tribes of Centaur at war, and you could choose to develop a relationship with either one. A popular challenge was to get exalted with one tribe to earn rewards, then grind to turn that reputation into hated and become exalted with the other tribe to earn their rewards.

Am I on track with what you’re looking for (albeit probably on a smaller scale?)

Yes! You’re spot on. I want to be able to have a branching story based on the decisions you make from other interactions and make new options of dialogue present depending on a previous interaction. I love the references by the way.

About a day later and I’m still not having much progress. I think it’s mostly because I can’t entirely wrap my mind around and see the bigger picture on how both of these systems can fully interact with each other on a level to where it can be easily configurable. Haven’t had this much trouble figuring out a coding issue like this in a while haha

What we need for this mechanic is some sort of affinity/reputation counter…

public class ReputationStore : MonoBehaviour, ISaveable, IPredicateEvaluator
{
     Dictionary<string, int> reputations = new Dictionary<string, int>();

     public void AddReputation(string token, int amount)
     {
           if(!reputations.ContainsKey(token) reputations[token] = amount;
           else reputations[token]+=amount;
     }

     public int GetReputation(string token)
     {
          if(!reputations.ContainsKey(token) reputations[token]=0;
          return reputations[token];
      }

     public bool? Evaluate(string predicate, string[] parameters)
     {
           if(predicate=="HasReputation")
           {
                string token = parameters[0];
                if(int.TryParse(parameters[1], out int level)
                {
                      return GetReputation(token) > level;
                }
          }
          return null;
    }

    public object CaptureState()
    {
          return reputations; 
     }

     public void RestoreState(object state)
     {
          reputations = (Dictionary<string, int>)state;
     }
}

The ReputationStore will go on the Player. Whenever something affects reputation, call AddReputation with the correct token and amount. To check the reputation in a condition, test HasReputation token amount.

Wow!! This is amazing. Thank you so much! I’m almost done implementing it, but I have a question in regard to this statement:

if(int.TryParse(parameters[1], out int level)
                {
                      return GetReputation(token) > level;
                }

For testing purposes, I am currently setting a bad choice to have 1 reputation, a neutral choice to have 2, and good 3. When I select a good option in the previous dialogue, it will show all 3 options (good, neutral, bad) in the next. I tried changing the greater than symbol to == but after testing, nothing would show up because GetReputation(token) returns “12” when in my mind it should only be “3” assuming I selected a good decision. Is there a way I could ensure I only get one response option per selection? (This issue also happens when I make bad choices result in a negative rep amount.)

By the way, I wasn’t sure where else I should put the call to AddReputation(), so I did it with dialogue triggers. Maybe this has something to do with it?


Edit:
After further testing, I found that all of the values for each choice end up showing even if I didn’t go over to the other person to make the decisions. (Note: I ended up making the reputation values different, as if I couldn’t make it any more confusing for you. Good rep is +15, and bad rep is -10.) The reason it’s not showing the neutral route value is because that one has a predicate of “HasInventoryItem”.

Hmmm… applying this to your specific circumstance could be tricky…
If you have more that one source of Reputation… i.e. I did a good deed for the inkeeper and slayed the rat in the basement, and brought the Knights who say Nee a shrubbery without resorting to saying Nee to old ladies at random, then the good deeds add up, so a specific score won’t always work… Most of these setups convert the current reputation into a ranking… For example below zero is disliked, below 100 is hated, above zero is neutral, above 100 is liked, above 500 is exalted…

So bad behavior (feeding the rat in the cellar, robbing the old lady to get a shrubbery, changing all the references in the credits to Moose) would get negative scores, bringing you into the disliked, a few good deeds might leave you still in neutral, etc…

Now how to apply this to the Conditions… I always like to describe conditions just like they are if statements…

So bad would be

if(!HasReputation("village", 0))

Neutral would be

if(HasReputation("village", 0) && !HasReputation("village", 100)) 

and Good would be

if(HasReputation("village", 100)

Okay! I’ll give that a shot, and I’ll update what ends up happening. What I really like about the system you’re sharing with me here is the fact that it can be applied to this specific instance, and not just as an overall reputation rating, but it could also be overall if I wanted to!
In fact, once I get this up and running, I might just go ahead and add that. Won’t be too hard at this point; just plug in a couple numbers and I’m set. But first, I will see what will work out with this specific instance.

Yeah, at this point I’m not sure if I’m getting closer or straying further away from what I need to do. I implemented this system within ReputationStore.cs as you can see here:

I’m getting 3 returns (probably all 3 dialogue options) printing “Good: 26”, so I don’t even know how that all adds up considering the dialogue path values have not changed since I made the edit in my last post:

Either way, I’m strongly tempted to now stick with keeping the dialogue triggers to be the ones to change the certain dialogue files on each separate character. That seemed to do the trick really efficiently on the player’s side of things. The only downsides to it are that it uses more files in the resources folder (which isn’t a ton), and it may look a little cluttered on the editor’s side. But it works, and I just don’t think this dialogue system that was built here was really meant to be used like how I’m trying to now. If you have any more suggestions on how we can further figure this out, Brian, I am more than willing to keep pushing through and finding an answer. But otherwise, I suppose I’m content with the state I had it at before. Thanks for all of your extensive help so far.

Privacy & Terms