Independent game developers - making Avoyd

Multiplayers toxic last hit kill and how to heal it

Doug Binks - 20 Oct 2017


Avoyd multiplayer swarm

Last hit kill scoring in multiplayer team based games feeds toxic behaviour by encouraging competition within teams over cooperation. I propose that we take a different approach to scoring where two equal player attacks at the same time do not result in one player arbitrarily reaping all the reward.

Many multiplayer games suffer from toxic player behaviour, and games developers are increasingly looking at creative solutions to problematic behaviour. For some great examples, see Jeffrey Lin's of Riot games talks on the GDCVault "The science behind shaping behaviour in online games" and "More science behind shaping behaviour in online games". Some of the problems Riot encounters come from the fact that League of Legends is a competitive game, but not all of them.

As a multiplayer cooperative game, Avoyd is likely going to suffer from problematic player behaviour. Without the resources of large studios and publishers we need to carefully ensure that the design works around pitfalls we've seen in other games.

What is last hit kill scoring?

Last hit kill scoring is an element of many games which feature player combat against non-player enemies (pve) or other players (pvp). Here the player to successfully score the last hit, thus dealing the killing blow, gets the majority or all of some form of reward.

Typically games track both the number of kills a player makes, and may in addition reward players who strike the last hit the greatest share of in game rewards such as currency for items and experience (xp) for leveling up abilities.

It's more than just kill stealing

The most obvious toxic consequence of last hit kill scoring is kill stealing - players who enter combat at the last minute to 'steal' a kill from someone who has dealt the majority of the damage. Players can also accidentally perceive other players as kill stealing when they just happen to be trying to help, leading to accusations or other potentially problematic behaviour between team mates.

More complex is that simply displaying the kill score results in a measure which acts as an incentive to play a meta game which has little to do with the main goal for the team, which can lead to player tension due to their different personal goals.

Do we need last hit kill scoring?

Anyone who's played a computer game with combat knows how important it is to remove an enemy from combat fully, rather than simply deal some damage. Last hit kill scoring can be seen as the reward for doing so. Indeed, many player roles are designed around assassin style combat with the intent that these players team up with another player who whittles the enemy down before the assassin jumps in and deals the final blow, usually at high risk to themselves as they have low defence abilities. Different combat styles can help stop games descending into a slug fest battle of attrition, and last hit kill scoring could be seen to act as an incentive to take on risk.

We can do better though, whilst preserving the risk / reward incentive and encouraging more cooperative team behaviour whilst removing potential for player tension.

Some games do this partially by awarding bonuses for kill assists. However this feels insufficient when a player's assist was to deal 99.99% of the damage.

A better approach, first step: Damage Tracking

To develop an improved approach we'll start by tracking damage dealt using the same units as entity health. Here we're talking about post defense (armour etc.) reduction damage as actually applied to the entity. We'll track how much damage is done by each player to an entity.

Taking an example where we have Alice and Barbara playing together as a team attacking a Drone. In this example the drone starts with a health of 100 prior to being damaged by the team.

Drone Current Health:     10
Alice Damage to Drone:    30
Barbara Damage to Drone:  60
Total Damage to Drone:    90

If a game features healing, this needs to be handled. The simplest approach I can think of is to reduce the damage score for each player by a fraction of the total damage which has been healed.

So for the damage done by each player we perform the following:


 New Damage = Damage - Damage * Healing / TotalDamage

In our example let's heal the Drone by 50 health points:

Drone Health:             60     = 10 + 50
Alice Damage to Drone:    13     = 30 - 30 * 50 / 90
Barbara Damage to Drone:  27     = 60 - 60 * 50 / 90
Total Damage to Drone:    40     = 90 - 50

This should correctly reduce the sum of all damage to an entity by H, and if an enemy is completely healed it correctly removes the kill score from anyone who attacked prior to the healing.

At this point no player has yet gained any additional score from damaging the drone, as it's not yet been killed. When a kill occurs we attribute a fractional kill score based on the fraction of damage done by the player divided by the total damage. For example Alice scores a blistering attack against the drone dealing 60 damage:

Drone Health:             0      = 60 - 60
Alice Damage to Drone:    73     = 13 + 60
Barbara Damage to Drone:  27
Total Damage to Drone:    100    = 40 + 60

Note that we only count actual damage done, so if the attack dealt more than 60 points of damage we only count up to the current health, not any over damage. So the fractional damage score for this kill would be:

Alice Damage Score:       0.73   = 73 / 100
Barbara Damage Score:     0.27   = 27 / 100

Simply scoring based on damage may not be enough to create the appropriate risk / reward incentive for a game. On its own it rewards low risk behaviour such as short attacks on multiple opponents rather than a single sustained attack on one.

Taking things further: Combat Bonus

One simple fix is to make the score nonlinearly proportional to the amount of damage such that the more damage done, the higher the score per unit of damage. This helps somewhat, but whilst it rewards dealing a large amount of damage, it doesn't reward persistent attacks on a single enemy until they are vanquished.

A better approach is to track the last time a player dealt damage, and start reducing their damage score after a given duration. This can be improved by tracking both the damage and a combat bonus score, with only the later being affected by time out of combat. For example, the combat bonus score could drop by 25% for every 15 seconds out of combat. As with the damage score, the combat bonus score on killing a enemy would be divided by the total.

Let's look at an example with Alice, Barbara and another Drone. First they both damage the drone, with Barbara again landing a huge blow of 60 and Alice only 10:

Damage Scoring:
Drone Current Health:     30
Alice Damage to Drone:    10
Barbara Damage to Drone:  60
Total Damage to Drone:    70

We'll track combat bonuses as 1 x damage:

Combat Bonus tracking for Drone:
Alice Combat Bonus:       10
Barbara Combat Bonus:     60
Total Combat Bonus:       80

The drone flees. Barbara is a slow heavy hitter so gets left behind. Alice is quick enough to follow, and lands an attack dealing 10 damage. Because time has passed, the previous combat bonuses reduce (here by half for demonstration purposes):

Damage Scoring:
Drone Current Health:     20
Alice Damage to Drone:    20
Barbara Damage to Drone:  60
Total Damage to Drone:    80

Combat Bonus tracking for Drone:
Alice Combat Bonus:       15  = 10 * 0.5 + 10
Barbara Combat Bonus:     30  = 60 * 0.5
Total Combat Bonus:       45  = 15 + 30

Alice stays in combat, and hits the drone with a killing blow dealing 20 damage. Little time has passed so the combat bonus does not decrease for either player:

Damage Scoring:
Drone Current Health:     0
Alice Damage to Drone:    40
Barbara Damage to Drone:  60
Total Damage to Drone:    100

Combat Bonus tracking for Drone:
Alice Combat Bonus:       35  = 15 + 20
Barbara Combat Bonus:     30
Total Combat Bonus:       65

Healing can be handled in a similar way to that for damage:


 New CombatBonus = CombatBonus - CombatBonus * Healing / TotalDamage

The final scoring is the sum of combat bonus score plus the plain damage score, with some multiplying score factor for each which we'll call CombatScore and DamageScore.:


 Kills = Damage / TotalDamage


 Score = CombatScore * CombatBonus / TotalCombatBonus
            + DamageScore * Damage / TotalDamage

This can be used for both the kill scoring and the in game reward contributions with potentially different factors for each.

If we have CombatScore = 900 and DamageScore = 100 giving a total 1000 points for a kill, from the last drone attack we have:

Alice:
Kills:      0.4   = 40 / 100
Score:      525   = 900 * 35 / 65 + 100 * 40 / 100

Barbara:
Kills:      0.6   = 60 / 100
Score:      415   = 900 * 30 / 65 + 100 * 40 / 100

Alternatively rather than showing the kill score from damage tracking you could show a share of the score for 'kills' along with the damage and score

Alice:
Damage:     0.4   = 40 / 100
Score:      525   = 900 * 35 / 65 + 100 * 40 / 100
Kills:      0.54  = 578 / ( 578 + 502 )

Barbara:
Damage:     0.6   = 60 / 100
Score:      415   = 900 * 30 / 65 + 100 * 40 / 100
Kills:      0.46  = 502 / ( 578 + 502 )

For an extra teamwork bonus show all player scores for a kill (to some cap). So Alice and Barbara would see:

Alice:
    YOU:      +525
    Barbara:  +415
Barbara:
    Alice:    +525
    YOU:      +415

Finally, if we imagine both Barbara and Alice attacked at the same time, dealing the same amount of damage but Barbara getting the final blow by a few milliseconds, both players would get the same score.

Conclusion

By tracking kill scoring through Damage, Healing and Combat Bonus we can derive fractional scores which share the rewards amongst all combat participants. The Combat Damage Bonus reduction from time out of combat keeps the incentive to take on risk. Since players share rewards, the system encourages team play. A kill steal scenario gifts rather than griefs.

Some experimentation is needed with this, and perhaps more factors are required to compute combat bonuses. Perhaps we need to track time in proximity rather than time since last hit, or a combination. Other effects such as enemy population in the vicinity could also be used to affect the Combat Damage Bonus scoring.

No longer should two equal player attacks at the same time result in one player arbitrarily reaping all the reward.


comments powered by Disqus
 › 2017
 › Speeding up Runtime Compiled C++ compile times in MSVC with d2cgsummary
 ›› Multiplayers toxic last hit kill and how to heal it 
 › Avoyd Editor Prototype
 › 2016
 › Black triangles and Peter Highspot
 › Colour palettes and lighting
 › Concept art by Rebecca Michalak
 › 2015
 › Internals of a lightweight task scheduler
 › Implementing a lightweight task scheduler
 › Feral Vector
 › Normal generation in the pixel shader
 › 2014
 › Python Google App Engine debugging with PyCharm CE
 › Lighting voxel octrees and procedural texturing
 › Patterns and spheres
 › Python Google App Engine debugging with PyTools
 › Interview
 › Domain masking using Google App Engine
 › Octree streaming - part 4
 › Black triangles and nervous_testpilot
 › Presskit for Google App Engine
 › Octree streaming - part 3
 › Octree streaming - part 2
 › Octree streaming
 › 2013
 › LAN discovery with multiple adapters
 › Playing with material worlds
 › Developer Diary archive
 › Website redesign
 › First Person Editor
 › First Avoyd tech update video
 › Implementing a static website in Google App Engine
 › Multiplayer editing
 › First screenshots
 › Thoughts on gameplay modes
 › Back in 1999
 › 2002
 › ECTS 2002
 › Avoyd Version 1.6.1 out
 › Avoyd Version 1.6 out
 › 2001
 › Biting the bullet
 › Avoyd version 1.5 out
 › Monday Mayhem
 › Avoyd version 1.5 alpha 1 out
 › Avoyd version 1.4 out
 › ECTS 2001
 › Fun with Greek letters
 › Closer just a little closer
 › Back already
 › Artificial Humanity
 › Products and promises
 › Ecommerce
 › Explosions galore
 › Spring fixes
 › Open source and ports to other operating systems
 › Avoyd LAN Demo Version 1.1 is out
 › Thanks for the support
 › Avoyd LAN Demo Ready
 › Game Tech
 › Speeding up Runtime Compiled C++ compile times in MSVC with d2cgsummary
 › Internals of a lightweight task scheduler
 › Implementing a lightweight task scheduler
 › Normal generation in the pixel shader
 › Lighting voxel octrees and procedural texturing
 › Octree streaming - part 4
 › Octree streaming - part 3
 › Octree streaming - part 2
 › Octree streaming
 › LAN discovery with multiple adapters
 › enkiTS
 › Internals of a lightweight task scheduler
 › Implementing a lightweight task scheduler
 › RCC++
 › Speeding up Runtime Compiled C++ compile times in MSVC with d2cgsummary
 › Web Tech
 › Python Google App Engine debugging with PyCharm CE
 › Python Google App Engine debugging with PyTools
 › Domain masking using Google App Engine
 › Presskit for Google App Engine
 › Implementing a static website in Google App Engine
 › Avoyd
 ›› Multiplayers toxic last hit kill and how to heal it 
 › Avoyd Editor Prototype
 › Black triangles and Peter Highspot
 › Colour palettes and lighting
 › Concept art by Rebecca Michalak
 › Feral Vector
 › Patterns and spheres
 › Interview
 › Black triangles and nervous_testpilot
 › Playing with material worlds
 › Website redesign
 › First Person Editor
 › First Avoyd tech update video
 › Multiplayer editing
 › First screenshots
 › Thoughts on gameplay modes
 › Back in 1999
 › Avoyd 1999
 › Developer Diary archive
 › Back in 1999
 › ECTS 2002
 › Avoyd Version 1.6.1 out
 › Avoyd Version 1.6 out
 › Biting the bullet
 › Avoyd version 1.5 out
 › Monday Mayhem
 › Avoyd version 1.5 alpha 1 out
 › Avoyd version 1.4 out
 › ECTS 2001
 › Fun with Greek letters
 › Closer just a little closer
 › Back already
 › Artificial Humanity
 › Products and promises
 › Ecommerce
 › Explosions galore
 › Spring fixes
 › Open source and ports to other operating systems
 › Avoyd LAN Demo Version 1.1 is out
 › Thanks for the support
 › Avoyd LAN Demo Ready