| 
  • 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 6: Auto-Resolve

Page history last edited by Little Alien 15 years ago

USUAL DISCLAIMER

The following article is about a rather complex system which is hard to explain in any orderly, easy-to-understand fashion. Therefore, I've allowed myself some reinterpretation of things, to make them sound simpler than they are really calculated. The most important stuff is kept intact, while some of it has been altered slightly so that it could be more easily explained. Most importantly, the turn system I describe functions in a slightly different manner. But the end result should be the same as predicted by this article, so worry not.

Also, again, this article is written in PSEUDO-CODE, it's not the real code of the game, but rather translated to simple english whereever possible.

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

The underlying concept behind the AutoResolve battle system is the use of a simulated battle without a battlefield, which nonetheless runs in real-time. Without a battlefield, such things as movement and Chance-to-hit must be simplified to the point where much of the result is randomized. While character skill levels do come into play, most of our equipment and weapon features do not. The end result is that autoresolve is not at all similar to tactical combat, but rather relies on numerical superiority.

The most important thing to understand about AutoResolve is that it does not really work in real time, but rather in a strange system of turns. In fact, it works by jumping along a timeline. Imagine a long timeline stretching from the beginning point of the battle ("Time 0"). At first, the program places all participants on this timeline in a certain sequence. The best fighters are first in the sequence (closer to the starting point), while reinforcements are last on the sequence. Then the program starts moving along the timeline, processing the actions it comes across. Each time an action is processed, the program may or may not create additional events further down the timeline. This way the program keeps moving along, processing actions and building future events, until one side is defeated.

However, the movement along the timeline is not smooth. The program makes small "jumps" along the timeline. Each such jump is called a "Time slice". There's no good way to explain it in words without a graphical representation, so here it is:

As you can see, the program has these "stopping points". It will jump from one to the next, and then process all actions that are between the old jump point and the new jump point. However, it will not execute them in the same order that they appear on the timeline, but rather randomize the order. So if we've jumped to a new point, and there are three actions that must be executed behind us, the program will execute one at random, then another, then the remaining action.

Each such "action" is basically an attempted attack by a specific combatant, assuming that character is still alive by the time he's scheduled to attack. When the action is processed, the character chooses a valid target and then tries to attack. The program then puts a new "action" for that character somewhere in the future, so that if he remains alive he can attack again, and again, until he dies or the battle ends.

The program will ALWAYS try to default to the use of firearms (guns) if it can, and only then uses knives, and if neither are available then it will use punches/blunt weapons. This makes sense because range is irrelevant in AutoResolve, and because knife/blunt/hand-to-hand suffers a certain penalty, as will be shown later.

When a gun is fired, the bullet does not hit immediately. Instead, it is assigned to fly towards the target over a short period of time. This is a very short period of time actually, so the bullet will reach the target right before it can fire back. There's a maximum of three bullets that can fly at a target simultaneously, although this is only a cosmetic limitation, as damage will simply pile on to those three bullets if many characters are shooting at the same target. So if 15 men are firing at one target, you'll have some of the bullets delivering massive amounts of damage. The limit of three bullets per target is simply a limit of the old JA2 program, and piling damage from several shooters on to a single bullet was simply their way of working around the limitation.

Finally, it is important to remember that the only factors we really care about are the character's skills (like Marksmanship, Wisdom, even the Medical Skill), our weapons' damage value, and the armor we are wearing. Everything else (like range and attachments) is meaningless. Therefore, even pistols can be dangerous in AutoResolve.

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

SETTING UP INITIAL VALUES

After determining how many enemies participate in combat (including all reinforcements), the function begins assigning "attack" and "defense" values for EACH PARTICIPANT.

These two values will determine chance-to-hit - the higher your Attack Value compared to the target's Defense Value, the more likely you are to hit the target. Also, a very important application of Attack Value is in determining who gets to shoot first, and how quickly you can shoot again after you've attacked. AP Costs are MEANINGLESS.

Let's start with mercenaries. Rememeber, we do this bit individually for each and every participant:

 Code:
For each Mercenary, do

This begins our loop. Everything beyond this point will be carried out once for each merc.

 

 Code:
Attack_Value = Merc's_Strength + Merc's_Dexterity + Merc's_Wisdom + Merc's_Marksmanship + Merc's_Morale

This is the basic value, calculated as a combination of 5 different skills and stats. Apparently, marksmanship and strength both apply, regardless of the weapon you are using.

 

 Code:
If Attack_Value < 1000, then
   Attack_Bonus = (1000 - Attack_Value) / 4
   Attack_Value is increased by Attack_Bonus

This is a bonus given exclusively for mercs. It's designed to compensate for the player's lack of control. The bonus is worth 1/4 of the way towards 1000 (the maximum attack value). That means, if the original Attack_Value was lower (lousy merc), the bonus would be greater. For instance, a merc who gets 500 in the initial calculation will get a bonus of 125 points (1/4 of the way from 500 to 1000). A merc with initial 800 points will get a bonus of only 50 points (1/4 of the way from 800 to 1000).

 

 Code:
Fatigue_Percentage = 100 - ( (100 - Merc's_Maximum_Stamina) / 3 )
Attack_Value = Attack_Value * Fatigue_Percentage / 100

Basically this measures how tired our merc is. The maximum_stamina drops when we get tired, and only comes back when we sleep. It's applied as a percentage to the Attack_Value, so we lose 1% of our attack value for every 3 points of tiredness (maximum_stamina below 100).

 

 Code:
Defense_Value = Merc's_Agility + Merc's_Wisdom + Merc's_Maximum_Stamina + Merc's_Medical + Merc's_Morale

Yup, defense bonus is based on the medical skill! Defense helps us dodge bullets, basically, so mercs with higher skills in these categories will have an easier time avoiding being hit in AutoResolve.

 

 Code:
Leadership_Bonus = 100 + (Group's_Best_Leadership / 10)
Experience_Bonus = 7 * (Merc's_Experience_Level - 5)
If mercs are encountering a mobile enemy group in the wilderness, then
   Player_Advantage_Bonus = 21

Total_Bonus = Leadership_Bonus + Experience_Bonus + Player_Advantage_Bonus

Attack_Value = Attack_Value * Total_Bonus / 100
Defense_Value = Defense_Value * Total_Bonus / 100

This is pretty straightforward. Three different bonuses are added up, and the result is used as a percentage modifier for both the Attack and Defense values.

To get the Group's_Best_Leadership value, the program searches amongst all mercs and militia to find the soldier with the highest Leadership skill. His leadership thus affects everyone's performance in both attack and defense. Each 10 points in the leadership skill add 1% bonus to attack and defense values.

Experience adds 7% for each level after 5. Conversely, it gives 7% penalty for each level below 5.

Finally, if the mercs have stumbled across a mobile enemy group in the wilderness, they get 21% bonus to their attack and defense. I have no idea why this is, maybe to simulate the fact that wilderness combat allows the mercs to encircle the enemy more easily than in cities? Again, this only applies when facing enemy PATROLS, not for stationary enemy groups like in cities and SAM sites.

 

 Code:
If merc is an EPC, then
   Attack_Value = 0
   Defense_Value = 1000

Thanks to Painy for discovering what an EPC is:

 

 Originally Posted By: Painy
I guess EPC means escorted PC (john, joey etc). they don't participate in combat so attackvalue=0 because they hide somewhere

Escorted characters include John and Marie, Maria, Joey, and of course Skyrider. Those are the NPCs who join your party but whose inventory cannot be accessed. Apparently, in autoresolve combat, they simply do not participate. Their attack value is 0, and their Defense value is always 1000, meaning that they are (or rather, should be) invulnerable, at least until the entire team guarding them is destroyed.

 

 Code:
If Attack_Value > 1000 then
   Attack_Value = 1000
If Defense_Value > 1000 then
   Defense_Value = 1000

Just making sure that neither goes above 1000...

 

 Code:
Group_Attack_Value is increased by Attack_Value
Group_Defense_Value is increased by Defense_Value.

Group Attack and Defense values are calculated from the total values of all participants on the appropriate side (I.E. militia are calculated together with mercs, as they are part of the same group). The group values aren't really important, they're just used as a tool by the program when selecting targets.

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

SCHEDULING THE FIRST ATTACK OF EACH PARTICIPANT

Next up, we're going to calculate a value called Next_Attack. This determines how soon this character gets to attack. The lower the value, the sooner we get to fire our weapon. It is based directly on the character's Attack_Value, although some of the participants will get a penalty to represent the time it takes them to join the battle.

 

 Code:
If Attack_Value < 200, then
   Next_Attack = 800
else
   Next_Attack = 1000 - Attack_Value

Basically, if our attack_Value is 1000 (the best possible), then our Next_Attack is immediate (0). Our next attack can't be higher than 800 at this point, although you'll soon see other penalties to this.

 

 Code:
Random_Number = Anywhere between 0 and (2000 - Attack_Value)
Next_Attack = 1000 + (Next_Attack * 5) + Random_Number

This pads our Next_Attack by at least 1000 points.

First we randomize a number. This number can be anything from 0 to 2000, although a good Attack_Value reduces the maximum, and thus reduces our chance to get a serious penalty to our Next_Attack sequence. If our Attack_Value was the maximum (1000) then the Random_Number is anywhere between 0 and 1000. Naturally, we want the Random Number to be low.

In the second line, we calculate the next_attack again, and we usually end up with a pretty big value. Again, we want to keep it as low as possible.

 

 Code:
If the merc we're checking now is the 9th merc in the sector, or more, then
   Delay_Penalty = 2000 * (Merc's_sequence after 8)
   Next_Attack is increased by Delay_Penalty

This part of the code simulates our mercs being somewhat spread-out across the battlefield. During auto-resolve, the sector isn't actually loaded into memory, so the program doesn't know anybody's placement. Instead, it allows up to a certain number of mercs (8) to attack as soon as their skills will allow, but if there are more than 8 mercs in the sector, the extra ones will suffer a penalty that simulates them having to run a little to reach the battle.

This only occurs if we've got more than 8 mercs participating in the battle. So, if the merc is 9th in the sequence, he gets a 2000 point delay to his attack. If he's 10th, he gets 4000 delay, and so on.

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

We repeat the above process to calculate attack and defense values for each militia and each enemy. For the most part it's the same, but I'll write the whole thing again so you can see the differences.

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

Starting with Militia:

 

 Code:
Attack_Value = Militia's_Strength + Militia's_Dexterity + Militia's_Wisdom + Militia's_Marksmanship + Militia's_Morale
Attack_Value = Attack_Value * Militia's_Current_Stamina / 100
Defense_Value = Militia's_Agility + Militia's_Wisdom + Militia's_Maximum_Breath + Militia's_Medical + Militia's Morale

For some reason we get use Current_Breath in the second line and then Maximum_Breath in the third line. Since militia and enemies never get tired (they can only get exhausted from being hit), maximum_breath is always 100. At this point in the calculation, Current_Health is probably also 100 (the battle has just started)...

 

 Code:
   Leadership_Bonus = 100 + (Group's_Best_Leadership / 10)
   Experience_Bonus = 7 * (Militia's_Experience_Level - 5)
   If militia are encountering a mobile enemy group in the wilderness, then
      Player_Advantage_Bonus = 21
   
   Total_Bonus = Leadership_Bonus + Experience_Bonus + Player_Advantage_Bonus

   Attack_Value = Attack_Value * Total_Bonus / 100
   Defense_Value = Defense_Value * Total_Bonus / 100

Same exact thing as with mercs. Militia even gets the player's advantage bonus for encountering enemy patrols in the wilderness.

 

 Code:
If Militia is GREEN, then
   Class_Modifier = 0.7
If Militia is REGULAR, then
   Class_Modifier = 1.0
If Militia is VETERAN, then
   Class_Modifier = 1.3

Attack_Value = Attack_Value * Class_Modifier
Defense_Value = Defense_Value * Class_Modifier

Based on the quality of the militia, his/her values may go up or down.

 

 Code:
If Attack_Value > 1000 then
   Attack_Value = 1000
If Defense_Value > 1000 then
   Defense_Value = 1000

Making sure they don't go over 1000.

 

 Code:
Group_Attack_Value is increased by Attack_Value
Group_Defense_Value is increased by Defense_Value.

Again, this is the calculated total of all attack and defense values for each and every militia in the group. They are totalled together with the mercs, as they are all on the same side of the battle. This is used later for targetting.

 

 Code:
If Attack_Value < 200, then
   Next_Attack = 800
else
   Next_Attack = 1000 - Attack_Value

Random_Number = Anywhere between 1 and (2000 - Attack_Value)
Next_Attack = 1000 + (Next_Attack * 5) + Random_Number

Again, calculating the next attack sequence. Works the same as with mercs, and again a lower result is better.

 

 Code:
If the militia we're checking now is the 7th militia in the sector, or more, then
   Delay_Penalty = 2000 * (Militia's_sequence after 4)
   Next_Attack is increased by Delay_Penalty

This is the same delay that mercs get when there are more than 8 in the sector, but with militia it is somewhat different. Each militia after the 6th will get a delay penalty (simulating them joining the battle later than others). But the penalty is higher than the penalty for mercs - the 7th militia member (first to get a penalty) gets a 6000 point penalty. The 8th militia member gets 8000, and so on.

 

 Code:
If the militia we're checking now is a reinforcement, then
   Next_Attack is increased by ( 1000 * Number of militia already in sector )

So basically, any militia who comes from an adjacent sector will get a penalty equal to 1000 points for each militia who was already in the sector. For example, if the sector has 18 militia, then any reinforcement gets an 18000 point penalty. This penalty only occurs at the beginning of the battle. After the reinforcement militia has attacked once, his next attack won't suffer from this penalty.

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

Now we do the same for enemies. The formula is exactly the same as militia, with three differences:

A) Enemies do not get the "Player's Advantage Bonus" of 21% for fighting in the wilderness. However, like Militia, they do get a bonus based on their level (ADMIN, TROOP, or ELITE).

B) Enemy Delay_Penalty starts at the 5th enemy, and is calculated at 1000 * (sequence after the 4th enemy). So the 5th enemy gets +1000 penalty, the 6th enemy gets +2000, and so on. This means that they do not suffer greatly from delay, but the penalty starts early for them - only 4 enemies avoid this penalty, while 6 militia avoid it, and 8 mercs avoid it.

C) Enemy attack and defense are added to the enemy group's_total_attack/Defense values.

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

Next we create a value called "Best_Attack". This value equals the lowest Next_Attack value we've calculated so far. So in essence, it equals the Next_Attack value of the fastest combatant. The program uses this to make the battle start sooner, to avoid an unnecessary wait for the bullets to start flying.

 

 Code:
Best_Attack = Find the lowest Next_Attack from all participants.
Best_Attack is reduced by 40%
For each participant in combat, do
   Reduce Participant's_Next_Attack by Best_Attack

I think this is just a cosmetic change - it makes the battle start sooner (I.E. less time waiting for the first shot to be fired). It doesn't change any of the game mechanics as far as I can tell.

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

By now we've got the Attack and Defense values of each participants. We also have a sequence of attacks, so the participant with the lowest Next_Attack value gets to shoot first. During the battle, participants will get the chance to shoot several times - their Next_Attack value will get recalculated after every time they attack. More about that later.

So without further ado, we're going to see how the program handles the actual battle.

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

BATTLE PROCESS

The diagram at the top of this article may be a bit confusing once we get down to business, because it's just a simplified way of looking at the Autoresolve Turn System. That's because we don't really jump forward in time, but rather we pull the actions back towards 0. Confusing? Don't worry, I'll explain it.

Each action has a certain timing. Let's say a character has a Next_Attack scheduled for 1800. Once combat begins, we actually reduce this number by 1000 points. In the next turn, we'll reduce it by another 1000 points, and so on and so on. Once the Next_Attack drops below 0, it is executed.

So when will our character attack?

In the first turn, we reduce the Next_Attack by 1000 points, so it now equals 800. The attack didn't happen yet.

On the second turn, we reduce it by another 1000 points. It now equals -200. It has just dropped below 0, so the attack occurs.

Every time we do a "time jump" we reduce each and every scheduled action by 1000. So instead of moving a vague "turn pointer" forward in time, we pull all actions backwards in time. I know, I know, this sounds really difficult to understand, but it's not all that complicated. I'll try another example, with a more "down-to-earth" approach.

Imagine you are holding a rope that has a lot of red markings on it. Each red marking indicates an action that will be executed in the future. Each "turn" of combat, you pull the rope one meter to you. So in the first turn, you pull the rope one meter, in the second you pull another meter, and so on. Each time you pull, the red markings on the rope keep getting closer and closer to you. Eventually, you're going to start passing these red markings - each time you do, it means that an action has to be performed. But since you pull exactly one meter at a time, no more no less, you can pass several red markings simultaneously. All of the red markings you've just passed during the last pull are executed together (like a single "turn"). Any red markings that are still ahead of you will have to wait some more. You keep pulling the rope, one meter at a time, until there are no more markings left (I.E. the battle is won).

If we've passed more than one action during a single pull of the rope, the sequence of these actions is randomized. This is complicated, so here's an example:

Our merc has a Next_Attack value of 800, and an enemy trooper has a Next_Attack value of 500. During the next turn, both their Next_Attack values will drop by 1000 points, putting them at -200 and -500 respectively. They are both now below 0, so both characters are due to attack this turn. However, even though the enemy had a lower Next_Attack value than our merc, there is no guarantee that he will fire first. Instead, the program will randomly choose who gets to go first this turn.

On the other hand, if our merc had a Next_Attack value of 999, and the enemy had a Next_Attack value of 1001, our merc will always shoot first. That's because in the next time slice, his value will be 999 - 1000 = (-1), which means he's ready to fire, while the enemy will get 1001-1000 = 1, which is not yet ready to fire.

Events aren't restricted to attacks though. In this system, bullets take a small time to fly. So once a bullet has been fired, it is assigned a certain timing somewhere further along the rope. The timing for bullet flight is very minimal - somewhere between 50 and 450, but in truth it is rather insignificant - the bullet will hit the target before it can shoot back, always. So flight time is mainly for cosmetic purposes (audio and visual effects).

Let's see how this works, with a bit of helpful pseudo-code.

 

 Code:
Every turn of combat, do the following:
---------------------------------------

For each action that hasn't been executed yet, do
   Action_Timing reduced by 1000
   If Action_Timing < 0, then
      Action is executed this turn!

I don't know why, but this makes so much more sense, doesn't it? \:\)

Now that we understand (HOPEFULLY!) how timing works, let's start combat.

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

During every "turn", also called a "Time Slice", the program will go into a loop that will process each and every participant, checking to see whether they were hit by any bullets and whether they are eligible to attack (I.E., their action is due this turn). Again, each and every combatant is checked, every turn, even the dead ones. Of course, only the live ones can actually attack during this check \:\) , and only if their Next_Attack is due now.

The combat-turn formula is very complicated, because it jumps back and forth to many different calculations, such as choosing a target or applying the effects of a bullet. Therefore, I will not be able to explain the whole thing in a linear fashion. I'll break the formula into several stages:

A) The "turn system" where each participant is processed.

B) Target selection - how does the program decide who to shoot at?

C) Attacking the target - how do we calculate a hit, and how much damage is applied?

D) Bullet impact - What happens when a character is hit?

E) Next_Attack calculation - how to determine when an attacker will get to shoot again later in combat.

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

THE TURN SYSTEM LOOP

First, we define the loop that will be executed each turn. This loop will run a number of times equal to the number of participants in the battle. In effect this means that each and every participant in the battle will be processed to check for hits, attacks, and future actions. Naturally, if the character is dead, then most of these will be skipped, but even dead characters are processed in this loop.

 

 Code:
Loop X times where X = number of participants (alive, dead, or otherwise)

Now that we're inside the loop, the program randomly chooses one of the participants:

 

 Code:
Randomly choose a participant who hasn't been processed yet.
   Mark him as "processed".
      Check to see if anything needs to be done with him this turn.
      Once done, select another participant.

Repeat until no more unprocessed participants remain.
Start a new turn.

This is a simplied version of the code, of course. We basically want to go through each participant in combat, and see if they get to do anything this turn (like shoot, get shot, or retreat). So we choose one, mark them as processed, go through the motions of checking what happens to this character, and then randomly select another character who hasn't been marked yet. The loop repeats this until there are no more unmarked participants.

We randomize the order in which participants are checked - This adds a nice random factor to battle. Note of course that simply being chosen doesn't mean the participant will be able to do anything - that depends on whether or not their Next_Attack is due this turn, and whether or not they are even alive (yes, as I said before, the program processes dead participants too!). We just makes sure we have some randomality.

This is especially important when two characters are supposed to carry out an action simultaneously (I.E. deciding randomly who gets to go first!). If, during this turn, three characters are due to make an attack, the random factor means that they don't get to go in any specific order.

 

 Code:
Attacker = The character we're processing now

From now on, the character being processed is called the "Attacker". Of course, we haven't even checked if he's eligible to attack this turn (heck, even if he is, he can still be killed before he gets to do anything, see below). But we'll call him the Attacker anyway. 'Cause I said so.

 

 Code:
If Attacker has not retreated yet, then
   If Attacker is the target of any bullet, then
      If Bullet is due to arrive now, then
         Hit the character

Bullets take some time to fly (we'll see how that is calculated later). If the bullet's flight time, after being reduced by 1000 (the "Time-Slice" value), is below 0, then the bullet will strike now (or miss the target). But as you'll see later, the flight time for bullets can't be more than 450, so if there are bullets in the air heading towards this target, then they will always hit now.

The damage done will be explained later in this article. We calculate the hit now because it can effect the attacker (or kill him) before he takes a shot. Again, all of this will be discussed later. Let's skip it for now and continue processing our character first.

 

 Code:
If Attacker has less than 15 health points, or has retreated from combat, then
   Attacker cannot do anything. He's finished his turn. Go and choose a new attacker.

This makes sure our attacker hasn't been removed from combat. Again, if the character has just been struck by a bullet and incapacitated or killed, this makes sure he won't also be able to attack from beyond the grave \:\) . And as I said earlier, the program also processes attackers who are dead, so this makes sure they don't get to do anything. \:\)

 

 Code:
If Attacker's Next_Attack is due now, then
   If attacker is due to retreat, then
      Reduce group's_Defense_Value by Attacker's_Defense_Value
      Reduce attacker's defense value to 0
      Mark attacker as having retreated from combat

This effectively removes the chosen participant from the battle. Note that the group's defense rating has dropped appropriately, to avoid problems with targetting (more on this later). I've never seen a character actually retreat from combat (except with the "Retreat Mercs" button)...

 

 Code:
If Attacker's Next_Attack is due now, and he hasn't retreated, then
   Target = Choose a target
   Attack the chosen Target
   Recalculate the Next_Attack value

Each of the lines above is a complex formula which will be explained separately below. The program chooses a target amongst the available enemies, then proceeds to attack the target, and then calculates a new point in the future where this attacker will get to shoot again.

And finally:

 

 Code:
Go to the beginning of the loop, unless we've already run it X times, where X = number of participants
If loop is over, end this turn of combat, and jump forward another 1000 points.

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

CHOOSE A TARGET

When the attacker is due for an attack, he or she must choose a target to hit. This is done by looking at each enemy and assessing whether they are valid targets (like whether they've retreated or already been killed). It is done in almost the exact same way for mercs/militia and for enemies, so I'll only cover choosing an enemy target.

 

 Code:
If Attacker is a Merc or Militia, then
   Available_Targets = Number of Enemies
   Total_Enemy_Defense = Enemy_Group's_Defence_Value

The Total_Enemy_Defense is simply a copy of the Group_Defense_Value of the enemy group. If you remember, it equals to the total defense values of all enemies who aren't dead/retreated yet. We make this copy because we're going to change it during this formula, but we still need to remember the original value for later. Remember that each enemy has anywhere between 1 and 1000 defense_value, so if the enemy team is full of 20 amazing Elites, the total will be 20000. It's just an example of course.

 

 Code:
For each Available_Target, do
   If Target is dead, then
      Skip this target.
   Else,
      Roll a number between 1 and Total_Enemy_Defense
      Effective_Enemy_Defense is reduced by the target's Defense_Value
      If Random_Number < target's Defense_Value, then
         Select this as our target.

This looks a bit complicated but it really isn't. Basically what it does is to increase the chance of enemies with a high Defense_Value to be selected as targets, while enemies with a low Defense_Value are less likely to be targetted. So you could say that the program wants you to shoot the best defenders first. The part that decreases the Total_Enemy_Defense is only there to make sure that we'll have to target SOMEONE eventually, so that the function never ends without a target being selected. This means that as long as your character can fire, he/she will fire at someone.

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

ATTACK A TARGET

Once the target's been chosen, it's time to try and shoot at it.

 

 Code:
If Shooter's_Attack_Value < 950, then
   Random Number = Anywhere between 0 and (2000 - Shooter's_Attack_Value)
   Attack_Value = Shooter's_Attack_Value + Random Number
If Shooter's_Attack_Value > 950, then
   Random Number = Anywhere between 0 and 49
   Attack_Value = 950 + Random Number

If Target's_Defense_Value < 950, then
   Random Number = Anywhere between 0 and (2000 - Target's_Defense_Value)
   Defense_Value = Target's_Defense_Value + Random Number
If Target's_Defense_Value > 950, then
   Random Number = Anywhere between 0 and 49
   Defense_Value = 950 + Random Number

We start out with a piece of code that seems pretty innocent. We draw the shooter's attack_Value, and the target's Defense_values, and modify them a little using some randomly rolled numbers. We'll later compare the two of them to see if the shooter can hit the defender. Unfortunately, this is where everything goes illogical.

You see, if the shooter's skills and abilities are pretty damn high, giving him an original Attack_Value of 990, it is cut down to 950 and then a small random number is added to it, between 0 and 49. So the shooter's result will be somewhere between 950 and 999. Great, that looks like a good number. It can beat pretty much any defender, right? Wrong.

Let's suppose the defender has 200 Defense_Value. This is a result of having poor abilities and skills, possibly very tired. However, the defender's formula means that he gets a value of 200 + random number between 0 and 1800. If we roll anything more than 800, we have a sure chance to dodge the bullet, regardless of the fact that the defender is pretty damn lousy! Roughly a 55% chance of that happening, too.

It also works the other way around, an attacker with lousy stats is actually too damn likely to hit a very skilled defender.

Also, in the current set-up, this puts WAY too much emphasis on luck instead of on skills. Fortunately, I've also made some repairs to this bit of code - the repairs are not available in 1.13 but may someday be. They reduce the number 2000 to 1000, and so the results depend less on luck and more on character skills. This means that mercs certainly don't die as often as enemies in autoresolve, as they are usually much more skilled than the enemy. In other words, the person with more initial Attack and Defence values has the advantage, as it should be.

Done with the rant, let's move on. Remember, we want to compare Attack_Value with Defense_Value later on. But there's stuff we need to do first.

 

 Code:
If Target is Retreating, then
   Attack Value is reduced by 30%

The logic behind this is that retreating targets are harder to hit. I don't exactly know how retreating is handled, and whether or not this has to do with the "Retreat Mercs" button. As far as I can see (and you'll see it later), retreating has something to do with getting shot, much like it is in the tactical game.

 

 Code:
If Attacker Can't Fire his Weapon, then
   Switch to Punch/Blunt weapon, but
   If Attacker has a knife, then
      Switch to Knife.
   If Target has a loaded weapon (anywhere in his inventory!), then
      If Attacker is now using a knife, then
         Attack_Value reduced by 40%
      Else (if Attacker is now using punch/blunt-weapon), then
         Attack_Value reduced by 60%

First you'll see the check whether the attacker can fire his weapon or not. To succeed in firing the weapon, you need to have either:

A) A loaded weapon, anywhere in your inventory, or

B) An empty weapon, anywhere in your inventory, and extra ammo for that weapon.

If the weapon is empty, the program automatically tries to reload the gun, and if it can do this, then the character is considered to have a loaded weapon immediately. If the gun has run out of bullets and has no reloads, the attacker fails to fire the weapon. Please note that the formula searches your ENTIRE inventory for a weapon, but it's always a good idea to hold your best weapon in your hands, as that's where the program looks first. Also note that the very check to see whether the soldier can fire will decrease the ammo in the magazine by 1 bullet.

Also note that if the attacker can fire his weapon, he will immediately gain 3 chances to level up in Marksmanship. This simulates the experience gain he would get for any shot in tactical mode.

Now, if we've failed to fire (I.E. have no weapon with matching ammo, anywhere in our inventory!), the program tries to switch to melee or knife combat. Knife combat is always selected if you have a knife.

Also, as you can see, attacking an armed target, while using a knife or blunt weapon, is much harder. The attack value goes down, and is now far more likely to miss, much more so if the attacker is trying a punch/blunt attack instead of a Knife attack.

Next, we set up a bullet in motion:

 

 Code:
If NOT attacking with a melee or knife weapon, then
   If Target doesn't already have 3 bullets flying towards it, then
      Assign new bullet to Target
      Bullet_Arrival_Time = 50+(random number between 0 and 399)

This bit creates the bullet that flies towards the target. It's a delayed effect, so the bullet will only hit in the next turn of combat. A target cannot have more than 3 bullets flying at it. If the target DOES have more bullets, we won't shoot a new bullet right now. The limit is very silly, I don't see any reason for it, possibly it's due to some old code to conserve memory, or who knows. However, we're not losing this shot altogether - we'll still calculate damage and add it to the damage of the LAST shot fired at this target. More on that later.

For now, also note that if a new bullet is assigned, it is set to arrive very soon (around 51 to 450 time units from now, which is usually in the next combat turn). The random factor here is just for cosmetics, to put a little variation in the exact timing of the hit/miss sound effect and visual effect. Since bullets never take more than 450 time units to fly to their target, then the next time we "process" the target, it will immediately be hit.

Now we check whether the bullet is actually scheduled to HIT or MISS the target.

 

 Code:
If Attack_Value is less than Defense_Value, then
   Random Number = anything between 0 and 4 
   If Target is still over 15 life, and Random Number = 0, then
      If Attacker is using Melee or Knife, then
         Target gains 3 chances to level up in Agility
      No hit! Bullet Damage = 0

Basically, if the attack value failed to surpass the defense value, then the shot will miss. However, if the target is unconscious, it will be hit regardless, unless it succeeds a 1 in 5 roll (I.E. a 20% chance to avoid being hit while unconscious).

In case of a miss, the function stops here. Well, actually, it just goes back to the Turn System loop (see earlier in this article) and picks a new attacker to process. If we're firing a gun, then the bullet that we've just generated remains with 0 damage, so it won't really hit the target.

Also, if we're using a knife or melee attack, the defender gains 3 chances to gain a level of agility for successfully dodging the attack.

Next up, if the shot is a hit, we assign a damage value to the bullet.

 

 Code:
If Attacker is firing a gun, then
   Basic_Damage = Weapon's_Inherent_Damage_Value
   Random_Number = anywhere between 0 and 99
   If Random_Number < 15, then
      Targetted_Bodypart = HEAD
   If Random_Number is between 15 and 29, then
      Targetted_Bodypart = LEGS
   If Random_Number is between 30 and 99, then
      Targetted_Bodypart = TORSO

So far so good, we draw the weapon's damage value out of the gun, and we roll a random number to see which bodypart we're shooting at.

 

 Code:
   Random_Number = Anywhere between 0 and (Defence_Value - Target's_Original_Defence_Value)
   HitBy = ( (Attack_Value - Defence_Value) + Random_Number ) /10

Here we calculate the HitBy value, which some of you may remember from my previous articles. This is a variable that tracks how successful the shot was compared to your Chance-To-Hit when firing it. In this formula, it is calculated out of the Attack and Defense values. We've already established that Attack_Value is higher than Defense_Value (otherwise, the bullet would've already missed, and we wouldn't even get this far in the formula). We add the difference between them to a random number that is calculated in a rather bizzare way. The random number is anywhere between 0 and the difference between the Defence Value and the Target's Original Defence Value. Remember, this difference is due to a random modifier we added at the beginning of the attack formula (it was anywhere between 0 and 2000-Defense_Value, and I got really mad about it, remember?). Finally, we divide by 10.

Why do we need a HitBy value? Ah, because of what we're going to do next!

 

 Code:
   Bullet_Damage = Calculate Bullet Impact based on all the variables we've described so far.

Go back and read "How does it Work" Part 1: Bullet Impact. The program uses that formula to determine how much damage will be done to the target. It uses the HitBy, Targetted_Bodypart, and Base_Damage values that we've just calculated. There are also special rules applied only to AutoResolve battles. Again, please read Part I if you want to know how bullet damage is calculated.

 

 Code:
If more than three bullets from any attackers are already flying at the target, then
   Add Bullet_Damage to the latest bullet flying at the target.
Else,
   Add Bullet_Damage to the bullet we've just fired

This is something I explained earlier. No more than three bullets can be flying at any target. If this shot is the 4th bullet (or more), the program simply adds the damage to the last bullet, instead of making a new one.

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

The next bit in the "ATTACK TARGET" formula handles damage done by hand-to-hand attacks. Such attacks happen immediately, so in essence it is a sort of combination between calculating the damage (like we just did with bullets) and applying damage to the target (as will be done when the bullet strikes), as well as giving experience to the attacker, and all the neccesary calculations related to the combat system. However, melee attacks almost never occur in autoresolve, mainly because the program looks for firearms ANYWHERE in the attacker's inventory before attempting to punch or use a knife. Only characters with no firearms in their inventory will be able to make these attacks in Autoresolve (or, with no ammo for their guns).

For the reasons stated above, I won't explain knife combat just yet. I'll hopefully add it to this article at a later time. Instead, I'm now going to cover what happens when a bullet hits the target.

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

AUTORESOLVE HITS

Much earlier in this formula, there was a short bit that checked whether any bullets are flying at the target. This was done way before the character had a chance to attack. It looked like this:

 

 Code:
If Attacker has not retreated yet, then
   If Attacker is the target of any bullet, then
      If Bullet is due to arrive now, then
         Hit the character

Now we're going to see what happens when the character is hit. Remember, we've already calculated the damage of the bullet when it was fired - now we only have to apply this. Also rememebr, if the bullet damage was set to 0 or less, it means no hit occured, but we still go through the formula nonetheless.

 

 Code:
If soldier is already dead, then
   Abort bullet damage.

This just makes sure we don't hit a dead target. This is because up to three bullet can be flying at any single target at the same time, so a previous bullet could've already killed the target by the time we get to calculate this one.

 

 Code:
If Target is a Creature, then
   Apply damage reduction based on creature size.

I won't go too deeply into this, just know that creatures get a damage reduction, just like they do in manual battles. Infants and Larvae get no damage reduction, young creatures get about 75% damage reduction, Adults get about 83% damage reduction, and the Queen gets the most damage reduction at 88%. Then again how the hell can you autoresolve during battle with the queen??

 

 Code:
Target's_New_Health = Target's_Health - Bullet_Damage

Basically reducing the target's health. We don't apply it just yet, we just want to know how much life the target will have left after it takes the hit. You'll see why this is important shortly.

 

 Code:
If bullet_Damage equals 0 or less, then
   If target is a merc, then
      Add 5 Chances to gain Points in Agility
   No damage!

Bullet damage will be 0 when the bullet is either set to miss, or simply hasn't been able to pierce the target's armor (based on the Bullet Impact Formula, explained in Part I of this series). If 0 damage occurs, a merc target will get 5 chances for agility level increase, due to "dodging" the bullet. In any case, the bullet has caused no damage. The formula then stops here and returns to the Turn System Loop, where if the character is still alive and is due for his Next_Attack, he'll be able to attack an enemy.

 

 Code:
From this point on, continue only if Bullet_Damage is greater than 0

If Attacker was a Merc, then
   Add 6 chances to gain a level of Marksmanship, for the shooter merc.

If the person who shot the bullet was a merc, their Marksmanship has 6 chances to go up a level. This is unlike tactical combat, where marksmanship increases depending on how successful the shot was. Since we don't have actual CTH here, the program just gives 6 points and avoids messy calculations.

 

 Code:
If Target is a Merc, then
   Experience_Bonus = 5 * Bullet_Damage / 10
   Get a number of chances to increase your Experience_Level, equal to Experience_Bonus

Getting shot will increase your chance to raise your Experience Level. Yes, that's right. And the more you're shot, the more experience points you'll get. For every 2 damage you suffer, you have 1 chance to gain an experience level. Interesting, huh?

 

 Code:
If Target's_Original_Health was better than 15, and Target's_New_Health is lower than 15, then
   Target is marked as Retreating

This is very strange. Basically, if the target's just been shot so bad that it has less than 15 life, it is marked as retreating. I don't know exactly what retreating is all about, since I've never seen a combatant withdraw from combat unless I tell them to (I.E. "Retreat Mercs" button). I really can't explain this bit. However, it might be possible that the word "Retreating" is simply wrong - perhaps the program means to say "unconscious"? I don't know.

Next, we're going to see what happens if the target's been killed.

 

 Code:
If Target's_New_Health has dropped to 0 or less, then
   Killer = Whoever shot the current bullet we're processing
   Assister1 and Assister2 = Whoever shot the other two bullets, if there are any, which are still heading towards the target

There can only be up to three bullets flying at the target simultaneously, and the program remembers who shot them. All three of these are considered to have helped kill the target, even if the target's been killed before the other bullets have struck.

 

 Code:
   Killer gets +4 Morale Bonus
   Assisters both get a few chances each to go up in Experience Level
   Killer gets twice as many chances to go up in Experience Level

There's something strange here which I don't understand. I'm going to quote directly from the code, so maybe someone can explain this:

 

 Code:
   StatChange( pKiller->pSoldier, EXPERAMT, ( UINT16 )( 10 * pTarget->pSoldier->pathing.bLevel ), FALSE );

This is the code that decides how many chances to advance in Experience Level. However, I have no idea what pTarget->pSoldier->pathing.bLevel is. Furthermore, upon testing, it seems that pTarget->pSoldier->pathing.bLevel always equals 0, so the shooters never actually get a chance to level up. What the heck is this??

Anyway, let's move on before we get too confused.

 

 Code:
Target's_Health = Target's_New_Health, but no less than 0

Ah, finally, the target's health has been damaged. It can't go below 0, of course, because 0 = DEAD AS A DOORNAIL. There's no deader than dead. \:\)

 

 Code:
If Target has been killed, then
   Reduce Group's_Attack_Value by Target's_Attack_Value
   Reduce Group's_Defense_Value by Target's_Defense_Value
   Target's_Attack_Value = 0
   Target's_Defense_Value = 0

Just correcting everything so that targetting works as it should.

And that's all there is to it. Remember that the program does this for each bullet that's flying towards the target. If the target has survived, it will now get the chance to fire back. Then the Turn System Loop goes on to select and process another participant.

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

RECALCULATE NEXT_ATTACK

After a combatant has attacked (and only if they've attacked), the program immediately calculates how long it'll take before the character can shoot again. This formula is the same one we used when calculating the initial NEXT_ATTACK values at the very start of the battle. However, this time it doesn't take into account reinforcement and delay penalties - this character has already fired a shot, which means he/she is already in the battle, and requires no further artificial delays.

 

 Code:
If Attack_Value < 200, then
   Next_Attack = 800
else
   Next_Attack = 1000 - Attack_Value

Random_Number = Anywhere between 1 and (2000 - Attack_Value)
Next_Attack = 1000 + (Next_Attack * 5) + Random_Number

So we get a new timing value, which represents how far in the future this character gets to attack again. Remember that a time slice is 1000 points.

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

 

Comments (0)

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