Skills based system

Well, then you have a bug somewhere because this is exactly what Fighter does, and that works. When the enemy you are attacking dies, it tries to find a new enemy in the vicinity and if it can’t, target is set to null. Attack over.

I think you may be looking at these skills from the wrong side (unless this is really what you want).

I personally wouldn’t award skill xp when the enemy dies. I’d rather award a small amount of xp each time a skill is successfully used. If I shoot the enemy with my bow and hit, my bow skill is awarded +1xp. If I then switch to a sword and whack him 2 more times, my sword skill gets +1xp for each hit. If I miss a swing, 0xp. If the enemy took 10 hits to kill and I shot him 4 times and hit him 6 times, my bow skill will have received +4xp and my sword skill +6xp. No need to keep track of which weapon was used the most. The amount of xp awarded is not linked to any enemy. It’s just 1 (or some other small value you would like to use). Killing an enemy that takes 10 hits to kill with a sword will result in a total of 10xp gained for that enemy. An enemy that takes 25 hits to kill will award a total of 25xp. And all this xp is neatly distributed across the skills I used to defeat said enemy.

If you do want to only award xp after the fight, I’d go with what Brian said and you’d have to remember that in some cases (like the 10 hit enemy above) you may have shot him 5 times and hit him 5 times and now you have a tie for the top spot. Which one gets the xp? Do you share? Does it not matter and you go with whichever the algorithm returns? Basically, there’s a huge chance that the weapon you used first will be the one that gets returned; 2 shots with bow, 3 hits with sword, 3 shots with bow, 2 hits with sword - tie for first, bow wins the xp because bow is first in the dictionary.

I just don’t want my player gaining XP in a battle he lost, so my solution was just to give off the xp ONLY IF HE WINS (what’s the point of getting XP if you’re losing the fight…? At least that’s how I want it in my game). For the case of two weapons that did exactly the same amount of damage, then the rewarding XP will be for the one that started it first. So if, for example, your ranged weapons did a total damage of 50, and your melee weapons also did a total damage of 50, but the ranged weapons started the fight/came up first, then the ranged weapons will get the XP (believe me, I have no idea how to implement that last part, but I’m down to give it some thoughts…)

It’s not xp for winning a fight. You don’t have that system anymore. You have a skill based system that gives xp for using a skill.

I just told you, it’s automatic. The first weapon will be first in the dictionary and therefore the one that will be returned

For combat, at least in my case, the skill is done when the player wins the fight… Any skill you basically only get the experience if you successfully accomplish the task, when you get the reward (ores for mining, logs for woodcutting, drops on the enemies’ death in combat, etc). A fight in progress is not a task done, it’s a task-in-progress, hence why I don’t want to give the player any XP yet :stuck_out_tongue_winking_eye: (otherwise I’d be giving the player woodcutting xp just for swinging a hatchet, mining xp for picking a rock but not getting resources, etc… Not the approach my game is doing tbh)

I have learned more from my failures than I have ever learned from my successes… In a skill based system, you should gain skill points by using the skill, even if the skill did not succeed. I spent nearly a year trying to play Rush’s La Villa Strangiato both on guitar and on bass. There are still sections I cannot play. In other words, technically, I failed. I did not slay the 8 minute masterpiece instrumental. But along the way, I learned incredible techniques that were used by Alex and Geddy, and it made me a MUCH better guitarist and bassist. If there was a Skill.GuitarPlaying and a Skill.Bass, I earned 50 levels in each one without ever accomplishing playing the whole thing without a flaw…

But the essential act of cutting a tree begins and ends with swinging a hatchet… The essential act of mining begins and ends with swinging a pick… Something to consider.

You could take the first result of the damage dictionary’s Keys property (an IEnumerable in this case).

private Skill GetFirstUsedSkill() => damagePerSkill.Keys.FirstOrDefault();

I don’t mean to come along as passive aggressive or defensive, but the whole idea was to make the game a little more difficult by doing so, by keeping the reward at the end. In other words, rewarding the player with XP in the end gives me more freedom to calculate how much experience they deserve for this fight, to ensure a smooth gameplay, or at least that’s my thought for now :slight_smile:

thank you for this as well by the way :slight_smile:

I think @bixarrio and I understand this quite well. Just pointing out a way that is actually used in many skills based systems.

Just remember you would only use this in the event that there was a tie between two skills… that being said, my original formula would not tell you if there was a tie, you would have to manually tally the values and check for a tie, then call this…
That being said, I’m 99.995% positive that the original query would return the first instance of damage in the event of a tie.

There is one other formula that might “spread the wealth” a bit…

         private void AwardExperience()
         {
                if (instigator.TryGetComponent(out SkillExperience skillExperience))
            {
                if (instigator.TryGetComponent(out Health otherHealth) && !otherHealth.IsDead())
                {
                    float totalAward = GetComponent<BaseStats>().GetStat(Stat.ExperienceReward);
                    float totalDamage = damagePerSkill.Values.Sum();
                    foreach (var pair in damagePerSkill)
                    {
                        float relativeValue = pair.Value / totalDamage;
                        skillExperience.GainExperience(pair.Key, Mathf.RoundToInt(totalAward/relativeValue));
                        
                    }
                }
            }
        }

This would divide the experience amongst all of the skills used, but still wait until the enemy has been well and truly slain to give experience.

Just a quick question on the fly… if ‘many skills based systems’ use it, don’t you think making it a little different makes for a good selling point? Again, I’m just looking for and listening to different perspectives here

For this one, I honestly replaced the new line you provided me with with the old one, so now my ‘GetMostUsedSkill()’ is like this (I renamed it to ‘GetMostDamagingSkill()’):

private Skill GetMostDamagingSkill() {
// return damagePerSkill.OrderByDescending(s => s.Value).First().Key;
return damagePerSkill.Keys.FirstOrDefault()
}

As I thought it was a replacement

As for the ‘AwardExperience()’ reward, my formula is slightly different, as I assigned each enemy his individual reward, rather than relying on the progression system, as I want various enemies, at various levels, to reward the player with various levels of experience, so mine right now will look like this:

private void AwardExperience(GameObject instigator, Skill skill, Skill otherSkill = Skill.Defence) {

            if (instigator.TryGetComponent(out SkillExperience skillExperience)) {

                if (instigator.TryGetComponent(out Health otherHealth) && !otherHealth.IsDead()) {

                    // This line sends 2/3rd of the XP Reward to whichever skill is associated to the Players' weapon:
                    skillExperience.GainExperience(skill, 2*Mathf.RoundToInt(GetComponent<AIController>().GetXPReward()/3));
                    // This line sends 1/3rd of the XP Reward to the defence XP, which is always 1/3rd of whatever the AI Reward XP is assigned to:
                    skillExperience.GainExperience(otherSkill, Mathf.RoundToInt(GetComponent<AIController>().GetXPReward()/3));

                }

            }

        }

This is before I tuned the function (again, I split my XP to 66% goes to the most damaging skill, and 33% goes to defence, so by default defence is now always being trained, and I might compensate for this benefit in other ways, like making levelling a little harder perhaps). After tuning, I believe it will probably look like this:

private void AwardExperience(GameObject instigator, Skill skill, Skill otherSkill = Skill.Defence) {

            if (instigator.TryGetComponent(out SkillExperience skillExperience)) {

                if (instigator.TryGetComponent(out Health otherHealth) && !otherHealth.IsDead()) {

                    float totalAward = GetComponent<AIController>().GetXPReward();
                    float totalDamage = damagePerSkill.Values.Sum();

                    foreach (var pair in damagePerSkill) {
                        float relativeValue = pair.Value / totalDamage;
                        skillExperience.GainExperience(pair.Key, 2*Mathf.RoundToInt(totalAward/relativeValue)/3);
                    }

                    // This line sends 2/3rd of the XP Reward to whichever skill is associated to the Players' weapon:
                    // skillExperience.GainExperience(skill, 2*Mathf.RoundToInt(GetComponent<AIController>().GetXPReward()/3));
                    // This line sends 1/3rd of the XP Reward to the defence XP, which is always 1/3rd of whatever the AI Reward XP is assigned to:
                    skillExperience.GainExperience(otherSkill, Mathf.RoundToInt(GetComponent<AIController>().GetXPReward()/3));

                }

            }

        }

In testing though, the mathematical values are completely out-of-whack, and I have no idea why (P.S: I want to keep this formula commented for now, I might want to swap systems later down the line… for now though, what’s wrong with it?)

That would, effectively, grant experience for the FIRST attack used, and wouldn’t be any fairer than using the LAST attack used…

Note that you have cached totalReward at the beginning of the if block. You should take advantage of that when dealing with the defense gain, and use Mathf.RoundtoInt(totalReward/3)

Can you be more specific. Provide examples of what is out of whack…
You might start by logging out each of the values as they are calculated.

Well… I did punch a guy a small punch, relevant to his total health, and somehow get 295 XP, when the enemy should not be giving me more than 20XP combined… I’ll test the mathematics for this again

How do you check for a tie though, do you just write this line in ‘GetMostDamagingSkill()’? :

private Skill GetMostDamagingSkill() {

return damagePerSkill.OrderByDescending(s = > s.Value).First().Key;

if (/*what do I write here, to compare between dictionary values...?*/) {
return damagePerSkill.Keys.FirstOrDefault
}
}

Any chance this punch was the last punch?
One thing I like to do is to change the damage from a killing blow to be the amount of health the big bad has left…
For example:

        public void TakeDamage(GameObject instigator, float damage, Skill skill)
        {
            damage = Mathf.Min(damage, healthPoints.value);
            healthPoints.value = Mathf.Max(healthPoints.value - damage, 0);

(P.S: Please check my previous comment, it’s edited) It’s not just the punch, it’s generally the entire system has been multiplied by something I guess… For instance, half the damage I did was in ranged, and the other half was in magic. Although my total XP was supposed to be 20, that is 7 for defence and 13 for the other two, somehow my magic got 30 XP (I’m guessing it did slightly more damage) and my Ranged got 24 XP, which is a total of 54 XP, 41 more experience points above my expected 13 XP points… is that foreach loop responsible for something…?

After a little more testing, the attack is specifically buffed out for some reason… because it did less than 40% of the total damage and somehow got 40 XP on its own…

This won’t get you there…
In fact, my original damagePerSkill.Keys.First would fail because you could start with one magic punch, and then do an equal amount of Melee and Ranged, and these would be the winner…

You’d have to have yet another tracking system

private List<Skill> damageOrder = new List<Skill>();

        private void AddDamage(Skill skill, float damage)
        {
            damageOrder.AddUnique(skill); //This will make a list in order from the first skill used to the last
            if (!damagePerSkill.ContainsKey(skill)) damagePerSkill[skill] = damage;
            else damagePerSkill[skill] += damage;
        }

       private Skill GetMostUsedSkill()
       {
            var pairs = damagePerSkill.OrderByDescending().ToList<>();
            if(pairs.Count==1) return pairs.Key;
            if(pairs[0]==pairs[1])
            {
                 for(int i=-;i<damageOrder;i++)
                 if(damageOrder[i] == pairs[0].Key) return pairs[0].Key);
                 if(damageOrder[i] == pairs[1].Key) return pairs[1].Key);
            }
            return pairs[0].Key; //It should never get here, but won't compile without it
       }

Here is where you gain experience from my error… :slight_smile:
The way to allocate proportion in a reward based on a proportion in effort is

ratio = effort/totalEffort;  (this will always be a value between 0 and 1, it's a percentage
reward = totalReward * ratio; (as Ratio is a percentage of the effort, award that percentage

Hence my error… That line should have read

 skillExperience.GainExperience(pair.Key, Mathf.RoundToInt(totalAward*relativeValue));

Now… when you add these numbers together, you are likely to be off anywhere from 1-3 points in total experience rewarded. This is because we’re rounding the numbers to the nearest integer. I wouldn’t be terribly concerned about this… in the beginning, it may look like a big deal, but when you get to the point where you’re dividing up 1000 experience for killing the big bad Jabberwock, a couple points in the total isn’t even slightly significant.

‘AddUnique’ returns an error…

Give me one second to try code this, there’s one more…

Why so it does… I’ve been working in C++ all day. (See, you just gained more experience from MY mistake). :stuck_out_tongue:
Try this:

if(damageOrder.IndexOf(skill)<0) damageOrder.Add(skill);

OK OK Fine lel… I’ll implement the system xD (I laughed so hard when I realized exactly what you just did to me…)

OK Serious note though… there’s a ton of errors. I’m not very familiar with dictionary context just yet, now is probably the worst time to test me on dictionaries… :sweat_smile: (can we please fix it, xD)

Not without knowing what the ton of errors are…

Privacy & Terms