| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • You already know Dokkio is an AI-powered assistant to organize & manage your digital files & messages. Very soon, Dokkio will support Outlook as well as One Drive. Check it out today!

View
 

"How does it work" Part 1: Bullet Impact

Page history last edited by Headrock 14 years, 3 months ago

There are many of us who want to know how the insides of the game works, but for most people reading the code itself is not an option. It can be confusing, it can be difficult to learn, and it's sometimes so complicated it can make your head explode.

 

Having some familiarity with the program, and being extremely curious about how certain things in the game really work, I decided to try and analyze the code and put it into simpler words. Even if I don't understand anything, I'll get the general feel of how things get calculated together. And, of course, if I can figure it out, I can share it with everybody else, so it's twice the fun.

 

This article will hopefully be the first in a series of articles about how some of the most important parts of the game really work. What factors into the calculations? What surprises are not obvious without reading the code? What can I do to increase my chances? Many of the myths and mysteries will hopefully be dispelled.

 

 

The first article I want to write is about Bullet Damage. This has mystified me for a long time, and now comes the moment to figure it all out.

 

This article will cover the function called "BulletImpact()". It runs whenever a bullet or thrown knife hits the target. This function does NOT cover explosives, nor does it cover Melee Combat with or without weapons. It is only applicable for guns and throwing knives.

 

PLEASE NOTE: The code written below is _NOT_ copied from the game. I did my best to try and explain the formula in simple words and in simple format, using what is called "Pseudo-code". Meaning, I tried to stay within the realm of the English language \:\) . Also, some things may have been ommitted because they were too complex or not very relevant. Other things may have been shifted around so that they are less confusing. But for the most part, this is a pretty solid representation of the code in a form much simpler to understand.

 

Have fun reading!

 

-------------------------------------------------

 

BULLET IMPACT

 

We start with several values which the program feeds into the calculation:

 

Potential_Damage - The amount of damage the bullet has at the time it hits the target. The bullet starts with a damage value equal to the "Damage" value of the gun that fired it. During flight, the bullet loses some of its power, so the range to target affects this value. Also, if the bullet has stuck on object or another target along the way, its remaining power is reduced.

As the formula proceeds, "Potential_Damage" will either increase or decrease.

 

HitLocation - Where the target was hit. I.E., the head, torso, or legs. Each hit location is handled differently, especially in regards to critical damage.

 

HitBy - This is the difference between the Chance-to-Hit and the random number rolled by the program to determine whether the shot hit the target. Naturally, if the rolled number was larger than the chance-to-hit, the bullet doesn't hit the target... But if the random number is equal to or smaller than the chance-to-hit, then the difference is recorded here.

 

Armed with these values, we can start working our way through this complex formula. Let's start with some casual checks.

 

 

 Code:
If the weapon used is a throwing knife then 
   Use Knife-Type Ammo Properties

 

The program starts by checking whether the attack was done with a thrown knife or other thrown weapon. If so, then the program reads data from the "Knife" ammotype. This is important because knives don't have ammo, so the program needs to be told where to read all the special ammo properties it needs. Other guns just read their own ammo's properties.

With the current XMLs, knives IGNORES ARMOR. I'll explain that property later.

 

 Code:
If (Target is a TANK) and (Ammo isn't AntiTank) then
   0 damage!

 

Naturally, only ammo that has the AntiTank property can hurt tanks. The program makes sure the current ammo can hurt tanks. If it can't, the damage returned is 0.

 

 Code:
Potential_Damage = Potential_Damage + anywhere between -25% and 25%

 

This is a completely random modifier (called a "FLUKE" modifier) that gives anywhere between -25% and +25% to the potential damage. This gives a nice variation of damage between shots, regardless of anything else.

 

 Code:
Potential_Damage = Potential_Damage + (HitBy / 2)%

 

Remember the "HitBy" value I spoke of before? It's the difference between how likely the shot was to hit, and the random number the program rolled to check whether the target was hit at all.

So if our shot was 99% accurate ("perfect" chance-to-hit) and the rolled number was 15, then the difference is 84. We'd then get +42% increase to our potential damage!

So while this is a random factor, it's obvious that if a shot was fired with better accuracy, there's a greater chance for it to do much more damage. Shots fired at 10% accuracy are not only unlikely to hit, but also unlikely to cause more than a few percent more damage than usual.

 

 Code:
If Potential_Damage < 1 then
   Potential_Damage = 1

 

A bullet can sometimes strike the target with 0 or less damage left, especially if it had to pass through an obstacle first. The program simply makes sure that it never has to work with values less than 1...

 

 Code:
If Ammo_Type is a High Explosive then
   Potential_Damage is multiplied by the "BeforeArmourImpact" value of the ammo

 

High explosive ammunition explodes right before hitting the target. This is done with MiniRockets HE and HEAP, but as yet isn't used with other items in 1.13. The value "BeforeArmorImpact" is read from the XML data of the bullet. This multiplication increases the potential damage of the bullet greatly, usually ensuring armor penetration and massive damage to the target.

 

 Code:
If Target is a Tank then
   Potential_Damage is divided by 2

 

Tanks suffer only half of the bullet's damage potential, if it can hurt tanks at all...

 


 

At this point, the calculation moves into another set of functions in the code, which calculates how armor interacts with the bullet.

Firstly, it finds the armor item that's been hit, depending on the bullet's HitLocation. It then reads all data about the armor's properties and status.

If the armor has any attachments, like ceramic plates, things get a bit more complicated, and I'll explain that later. For now let's assume we're hitting an unmodified piece of armor.

The program reads three pieces of data from the armor:

A) Armor_Status - the current status of the armor between 100% and 1%

B) Armor_Protection - how strong the armor is in its current state

C) Armor_Coverage - how much of the target's body this piece of armor covers.

Also we add a fourth variable called Total_Protection which will track the changes in the armor's effectiveness.

Then, we begin the formula. Firstly, the program rolls a random chance to see whether the bullet hit the body areas that the armor doesn't cover:

 

 Code:
Pull a random number between 1 and 100.
   If Random Number > Armor_Coverage then
      If armor is a vest and the bullet can't "ignore armor" then
         Bypassed armor, but hit non-vital area. Total_Protection = half the bullet's strength.
      Else
         Total_Protection = 0. Armor completely fails to stop the bullet!

 

So essentially, armor with a lower "coverage" value is less likely to stop bullets at all. Also you can see that if the target's torso is hit in uncovered areas, the armor manages to stop only half the bullet's damage. We consider this to be a hit on the arms or other non-vital areas, usually protected by less armor or none at all.

Next up, the program checks to see whether the bullet hit a "weak-spot" in the armor. It does this by checking armor status. After all, if the armor's been shot in the past, it will have lower status, and hence more weak spots:

 

 Code:
Pull a random number between 1 and 100.
   If Random Number > Armor_Status then
      Total_Protection is reduced by the difference
      and also:
         If Total_protection has just dropped below 0 then
            Armor completely fails to stop the bullet!

 

This means that if status is lower, the bullet is more likely to hit a weak spot. If a weak spot is hit, the armor becomes less effective (how much less depends on the difference between Random Number and Armor_Status). Naturally, if the shot was very powerful and very lucky, the bullet goes right through the weak spot, uninterrupted!

Next up, the bullet's armor-piercing properties (or lack thereof) take effect:

 

 Code:
Total_Protection is multiplied by the Bullet's Impact_Multiplier, and divided by Bullet's Impact_Divisor

 

The two values above are drawn from the bullet's XML data. With common Armor-piercing bullets, the modifier is 0.75, meaning that armor_protection is 75% of its original value (or, in other words, 25% less effective). With common HP bullets, it's the other way around - armor becomes STRONGER against them. With regular ammo ("Ball" or grey-colored ammo), the value is 1.00 - it doesn't change the effectiveness of the armor.

Immediately after this, acidic properties take effect, reducing armor status. Acidic ammo, like bug spit, degrades armor very quickly. To do this, the program reads the armor's "Degrade percentage", a value that determines how easily this armor is destroyed compared to other armors

 

 Code:
If Ammo is Acidic then
   Armor status degrades by (3 * armor_protection * armor_degrade) / 100
   And also:
      Total_Protection is reduced by half!

 

Basically this means that the higher the armor's Degrade Rate, the more damage it received from spit. Surprisingly, the stronger the armor, the more damage it receives! That's just weird. And acidic ammo can easily penetrate armor, as youc an see above, the armor's effectiveness in stopping the attack is reduced by 50%! Much better than Armor-piercing bullets!!

Bullets may also have an "Ignore Armor" property, which is calculated next:

 

 Code:
If Ammo can Ignore Armor then
   If we hit the vest or leggings then
      Total_Protection = 0. Armor completely fails to stop the attack!

 

At the moment, only knives and darts can ignore armor this way. And as you can see, it's apparently impossible to ignore helmets. Odd, but that's the way it is. Also note that ceramic plates and other attachments CAN stop these attacks - only vest and legging armours are avoided by Ingore_armor bullets.

Finally, much like the "Acidic" bit above, the armor's status now degrades due to the bullet's impact:

 

 Code:
Armor status degrades by (armor_protection * armor_degrade) / 100

 

This is the same as above with "acidic" ammo, except this time it's not multiplied by 3. This is used for all ammo types of course - any attack will reduce the status of the armor it hits.

Finally, the program returns the Total_Protection value, or what's left of it after all the above calculations. We'll see how that total_protection is handled later. First let's see what happens if the armor item had attachments.

 

If the piece of armor we hit has attachments, then things behave slightly differently than above. Each armor attachment has a certain chance to stop the bullet before it hits the armor itself. Note that when I say "armor attachments" I mean stuff like Ceramic Plates or Leg Protectors which have their own protection values. Stuff like sun goggles, while it can be attached to some helmets, can't help in stopping bullets.

The program goes through the attachments one-by-one. It performs much the same as it did above for each attachment. The results (the "Total_Protection") are added up later.

There's one thing to know about attachments though. If the coverage of the attachment was sufficient, and the bullet didn't bypass it completely, then ALL SUBSEQUENT ATTACHMENTS are assumed to have 100% coverage, as well as the armor itself! Meaning, the bullet can't hit an attachment and then bypass the armor itself. If it hits one, it hits them all.

 

Right. Now that we have all the Total_Protection values of all the armor pieces hit, we can move on with the damage calculations - namely, figuring out how much damage the bullet has left after hitting the armor.

First of all, the program actually destroys any armor piece that has gone below 10% condition.

Now, we sutract the combined Total_Protection values we got from the armor checks, from the bullet's original power:

 

 Code:
Potential_Damage reduced by Total_Protection

 

At this point, if the remaining power is greater than 0, that damage will eventually get transferred to the target's body and cause actual pain. However, the program first needs to make sure the bullet's remaining power doesn't go below a certain minimum:

First, we check the bullet to see if it has the "0 minimum damage" property.

 

 Code:
If Bullet is "Zero_Minimum_Damage" then
   If Potential_Damage < 0 then
      Potential_Damage = 0

 

Bullets that have this property can cause 0 damage to the target if they've encountered sufficient armor. HP and Glaser ammo have this - the bullet is so weak that armor can actually completely prevent it from doing damage to the body.

But this bit goes on, for bullets that don't have "Zero_minimum_damage". This bit is a bit more complicated, and I don't understand the reasons behind most of this, but here it is anyway:

 

 Code:
If the bullet is NOT "Zero_Minimum_Damage" then
   If Potential_Damage < ( Original_Bullet_Power + 5 / 10 ) then
      Potential_Damage = ( Original_Bullet_Power + 5 / 10 )

 

Odd stuff. Again, I'm not sure what the heck this is all about. I guess the bullet's strength can only be reduced by a certain amount of what the bullet originally had (including the "fluke" bonus and the "accuracy" bonus, which are explained much earlier in this formula).

There's a bit here that checks something called "gfNextShitKills". I think it has something to do with cheat codes, so I won't explain it here. But basically, if the flag is true, the shot gets 100 potential damage, which is probably enough to kill anyone. I have no idea what triggers this.

Right, back to the code. At this point, if the bullet has 0 potential damage, we're done. The bullet causes no damage at all except whatever it did to the armor. If the bullet does have some potential damage left, now is the time to check how severe it really is.

First, we handle dart ammunition:

 

 Code:
If ammo is a dart, then
   If HitBy is over 20 points then
      Put target to sleep :)
   Then, regardless of HitBy
        Hit the target with whatever damage is left

 

Darts don't always perform their special function - they require a good hit with high accuracy to have an effect. The "HitBy" value is explained near the top of this post. At this point, darts always deliver their full damage - they do not go on to check for critical hits and such. For them, the formula ends here.

For other bullets besides darts, we now apply the ammo's inherent "AfterArmorImpact" modifier, which is drawn from the ammo's XML data:

 

 Code:
Potential Damage is multiplied by Bullet's After_Armor_Impact_Multiplier, 
then divided by Bullet's After_Armor_Impact_Divisor

 

HP and Glaser ammo have high numbers here - the bullet's potential damage is multiplied by almost 2 with Glaser ammo, causing MASSIVE damage. Of course, that's assuming the bullet hasn't encountered too much armor resistance, especially because HP and Glaser are terrible at armor piercing, and may end up with 0 potential damage at this point in the formula... 0 * 2 is still 0.

Now, we apply a multiplier based on the hit location:

 

 Code:
If HitLocation = Torso then
   Critical_Potential_Damage = Potential_Damage
   Potential_Damage unchanged
If HitLocation = Head then
   Critical_Potential_Damage = Potential_Damage * 1.5
   Potential_Damage = Critial_Potential_Damage
If HitLocation = Legs then
   Critical_Potential_Damage = Potential_Damage / 2
   Potential_Damage = Potential_Damage / 4

 

This new value called "Critical_Potential_Damage" is used later for calculating whether a critical hit occured. The actual damage of the bullet doesn't increase in case of critical hits, but it can cause special effects and/or stat loss.

Hits to the head deliver 1.5 times more damage, also when determining whether critical damage occured.

Hits to the legs 25% as painful as other hits, but for the purpose of critical hit calculations, it's only half the potential damage.

You can easily see why headshots are so powerful, especially later when we determine whether the hit was critical. Also, you can see that leg damage is usually pretty light. Of course, leg damage will usually knock the target down too...

 


 

At this point the program goes into a "Special Criticals" check. This check will tell us whether the target has suffered a special effect due to the damage inflicted. In the case of head shots, it can cause the head to explode. In the case of chest-shots, it can cause the chest to explode. If the legs were hit, it can cause the target to fall over.

The actual formula for this is a bit complicated, and I don't completely understand it. It would be a rough guess on my part to try to explain how it works here, so I won't. Most importantly, you need to remember that the shot has to cause a certain minimum amount of damage to cause these effects. To blow a head off, you need at LEAST 55 points of damage left after all previous reductions. For chest explosions, it's about 85 minimum. To make the person fall over, you need to hit the legs with at least 20 damage, after armor.

There are also lots of other factors in this calculation, which is why it is so complex. Distance to target, bodytype, and other factors all affect whether the critical effect takes place or not. Again, to explain all this would be too complicated, so I'll skip it, at least for now.

 


 

The rest of the formula handles damage done by lucky critical hits. Please note that during Auto-Resolve battles, critical hits are impossible. If the battle is auto-resolve, the formula stops here and returns the current Damage_Potential. Otherwise, it goes on.

First, we check to see whether this was a stealthy knife attack. Stealthy attacks with knives (meaning, the enemy hasn't spotted you) are likely to cause critical damage.

 

 Code:
If Ammo Type = Knife then
   If the target can't currently see you then
      If the HitLocation is either the Head or Torso then
         Roll a random number between 1 and 100
            If Random Number is less than (HitBy, plus 10 for each THROWING skill level)
               Potential_Damage = Target's Health + 10
               Critical_Potential_Damage = Potential_Damage

 

Remember "hitby"? That's the number that tell us how successful our attack was, compared to our Chance-to-Hit. So the more accurate your attack was, the more likely it is to kill the target instantly!

Right, after determining that, we can move on to see how other bullet types behave in regards to critical damage. This bit is somewhat complex, so let's make a calculation first:

 

 Code:
Chance_for_Critical_Hit = (Critical_Potential_Damage / 2) + (AimingTime * 5)

 

The higher the damage done to the target, the better chance for a critical hit. Additionally, the more time you spent aiming at the target, the higher chance for criticals!

Also, because head-shots have a higher Critical_Potential_Damage value (1.5 times the actual damage of the bullet), they are more likely to cause a critical hit.

 

 Code:
Roll a random number between 1 and Chance_For_Critical_Hit
   If Random Number is larger than 30 then
      Critical Hit!

 

By now this should look familiar. If "Chance_For_Critical" was less than 30 to begin with, we're not going to see a critical hit. The higher it goes, the more chance we have to do some stat damage to our target.

If criticals have failed, the formula is done - it returns the current Potential_Damage, and the game continues.

Else, we move on to see exactly what kind of damage, and how much, is inflicted on the poor bastard.

 

 Code:
Roll a random number between 1 and (Critical_Potential_Damage / 2)
   Stat_Change = Random Number

 

Here the program determines the effect this hit has on the target's skills. You know, when your characters get hit in the head and lose some wisdom? This is where it's decided. You can see that the power of the bullet determines how many skill points can be lost, but the result is random within that range.

After that, there's a lot of lines of code, but what they simply do is to reduce the target's skills based on the area hit:

If the head is hit, reduce wisdom by Stat_Change.

If the torso is hit, reduce either Strength or Dexterity by Stat_Change. There's a 50/50 chance for either skill to be damaged, but not both.

If the legs are hit, reduce Agility by Stat_Change.

 


 

And that's pretty much it. At the very end of the function it simply returns the Potential_Damage value, and that is applied to the character's health.

Please note one more thing - any hit, regardless of how powerful it was, will reduce the target's stamina by a certain amount. That is _NOT_ covered in this post, and I may cover it later.

 

Comments (1)

Headrock said

at 3:30 am on Dec 15, 2009

Wow, this is formatted very badly.

Ironically, I don't have permission to edit it. :D :D :D

You don't have permission to comment on this page.