Again on killing more enemies

I followed the discussion here: Tracking kills for Quests?
But there are some things that still obscure to me :sweat_smile:

  1. How do I specify which enemy types to kill, I also considered to add a the CharacterClasses, but Iā€™ll do that in a second moment, when everything will works fine.

  2. I need help on PredicatePropertyDrawer class:
    how do I add a text field here to specify the enemy to be killed?

if(selectedPredicate == EPredicate.HasKilled)
{
    position.y += propHeight;
    DrawIntSlider(position, "Qty Needed", parameterOne, 1, 100);
    position.y += propHeight;
}

Because when I add it, the Remove button get over the slider so the slider value get covered.

@Brian_Trotter Iā€™ve followed you Achievment implementation and by debugging it, looks fine, I mean the KillSkeleton get raised as it should but the objective never gets completed.
image

The tracking of the relavent enemies for any given kill quest was achieved by placing a death counter on the enemy in question and providing their unique quest paramater ID which called to the counter class on the player when the monster died and notified the playerā€™s counter class of the death so it could be incremented. The quest then queried this counter class whenever the status of the quest needed to be updated to see if the player had actually met the requriements for number of kills by asking how many of those enemies the player had killed (again this is stored on the counter class on the player). Hopefully this helps?

So the Identifier is the QuestID and not the ObjectiveID?
I still confused, because in that case, how the Quest knows that killing this monster is about that particular Objective?
Tested, didnā€™t work in any case :roll_eyes:
[Edit]
Ok the Dictionary didnā€™t exist once the quest get acceptedā€¦
So in if (onlyIfExists) return 0; will exit.

The quest has a condition on it such as PredicateType ā€œHas killed.ā€

This condition has a parameter[0] which contains the name of the enemy to be killed.

The condition has a parameter[1] which is an integer value for the number.

In the previous thread a counter class was introduced that resides on the player. It contains keys for values corresponding to a string token (which in this case would be the monsters name) and a value (in this case the number of monsters killed by the player).

The counter has a predicate evaluator and it can evaluate the dictionary based on the parameter 0 for the token key to lookup and a parameter 1 for the number of enemies killed corresponding to that token.

public bool? Evaluate(EPredicate predicate, string[] parameters)
        {
            if (predicate == EPredicate.HasKilled)
            {
                if (int.TryParse(parameters[1], out int intParameter))
                {
                    RegisterCounter(parameters[0]);
                    return counts[parameters[0]] >= intParameter;
                }
                return false;
            }
            return null;
        }

When the player picks up a quest, you just register the questā€™s kill token with the counter.

You mark the enemies with a death counter script that can call the playerā€™s counter and increment their particular token on die events.

The quest list which contains the players quests can query the counter on the player for any particular quest to see if that specific quests conditions have been met by checking the quests parameter[0] in the counter dictionary and see if dictionary[parameter[0]] > questā€™s parameter [1] (aka the total amount needed to complete the quest.

How you choose the complete the quest when the player meets the requirements is up to your imagination. Have it happen when the quest finished immediately? Wait for the player to return to the quest giver? Wait for the player to enter a new zone? Etcā€¦

So itā€™s all about the token in RegisterCounter, but because I didnā€™t have a text-field in the condition of the quest, it will never run I guess :thinking:

RED = TOKEN
BLUE = MONSTER ID
OTHER BLUE = # TO KILL

Iā€™m taking about this one, inside the quest SO
image
Because the Enemy are set for the Objective as need.

:exploding_head:

Ok, can you show me the piece of the code where the Name is set in the Editor? :sweat_smile:

This is the OnGUI call:

I REMOVED alot of the other ā€œif selected predicateā€ to limit the size of this post.

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            SerializedProperty predicate = property.FindPropertyRelative("predicate");
            SerializedProperty parameters = property.FindPropertyRelative("parameters");
            SerializedProperty negate = property.FindPropertyRelative("negate");


            float propHeight = EditorGUI.GetPropertyHeight(predicate);
            position.height = propHeight;
            EditorGUI.PropertyField(position, predicate);
            position.y += propHeight;
            EditorGUI.PropertyField(position, negate);

   

            if (selectedPredicate == PredicateType.HasKilled)
            {
                position.y += propHeight;
                DrawInputString(position, parameterZero);
                position.y += propHeight;
                DrawIntSlider(position, "Kill Amount", parameterOne, 1, 100);
            }
        }

This is the draw input function:

 void DrawInputString(Rect position, SerializedProperty element)
        {
            EditorGUI.BeginProperty(position, new GUIContent("Name"), element);
            element.stringValue = EditorGUI.TextField(position, "Name", element.stringValue);
            EditorGUI.EndProperty();
        }

This sets up the property height correctly: Notce has killed requires *4f.

 public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            SerializedProperty predicate = property.FindPropertyRelative("predicate");
            float propHeight = EditorGUI.GetPropertyHeight(predicate);
            PredicateType selectedPredicate = (PredicateType) predicate.enumValueIndex;
            switch (selectedPredicate)
            {
                case PredicateType.Select: //No parameters, we only want the bare enum. 
                    return propHeight;
                case PredicateType.HasLevel: //All of these take 1 parameter
                case PredicateType.HasCompletedQuest:
                case PredicateType.HasQuest:
                case PredicateType.HasItem:
                case PredicateType.HasItemEquipped:
                    return propHeight * 3.0f; //Predicate + one parameter + negate
                case PredicateType.HasCompletedObjective: //All of these take 2 parameters
                case PredicateType.HasItems:
                case PredicateType.MinimumTrait:
                case PredicateType.HasKilled:
                    return propHeight * 4.0f; //Predicate + 2 parameters + negate;
            }

Let me know if that helps fix the issue for your inspector.

1 Like

It is working, and Iā€™ve also learned a bunch about the Editor.
Now I can add also Kill CharacterClass!
Thank you so much!
:smiley: :beers:

1 Like

Absolutely a pleasure my friend! Happy to hear you got it working :slight_smile:

1 Like

Iā€™ve ha have Idea about spawning NPCs but I wonder we have some method to read the Name from the Completion Condition inside the scriptable object, or in some other way, because I would need to set ti in side the spawned NPCsā€¦ :thinking:

A scriptable object is a data container.

There is no way to instantiate one at runtime. Atleast not one that will make it into a live build. (Create instance is only usable within the editor).

If you are spawning NPCā€™s from a prefab, then just set the quest itself as a parameter in the prefab and access that when the npc is instantiated at runtime.

Thatā€™s a very easy way to do it.

1 Like

The player have the QuestList component, my hope was to access to the variable from there :sweat_smile:

You could do it that way. You just need to create a function that would return the condition for the given quest. Completely viable if you want to have it centralized on the player.

1 Like

Going through this thread, itā€™s been a pleasure to watch the interaction and working through of the problem, especially on a topic that is outside of what was taught in the course! Well done all around!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms