Home » MODDING HQ 1.13 » v1.13 Modding, Customising, Editing » v1.13 Time Capsule (How-to Library) » "How does it work?" Part 6: Auto-Resolve
"How does it work?" Part 6: Auto-Resolve[message #197021]
|
Tue, 23 September 2008 11:01
|
|
Headrock |
|
Messages:1760
Registered:March 2006 Location: Jerusalem |
|
|
<font size="">USUAL DISCLAIMER</font>
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:
This begins our loop. Everything beyond this point will be carried out once for each merc.
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.
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).
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).
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.
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.
If merc is an EPC, then
Attack_Value = 0
Defense_Value = 1000
Thanks to Painy for discovering what an EPC is:
PainyI 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.
If Attack_Value > 1000 then
Attack_Value = 1000
If Defense_Value > 1000 then
Defense_Value = 1000
Just making sure that neither goes above 1000...
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.
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.
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.
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 ( 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:
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)...
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.
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.
If Attack_Value > 1000 then
Attack_Value = 1000
If Defense_Value > 1000 then
Defense_Value = 1000
Making sure they don't go over 1000.
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.
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.
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.
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).
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.
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.
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.
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.
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:
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.
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. :vader:
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.
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.
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)...
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:
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.
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.
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.
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.
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.
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
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:
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.
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.
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.
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!
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.
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:
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.
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.
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??
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.
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.
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.
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?
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.
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.
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:
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.
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.
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.
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.
--------------------------------------
[Updated on: Mon, 27 April 2015 23:43] by Moderator Report message to a moderator
|
|
|
|
|
|
Re: "How does it work?" Part 6: Auto-Resolve[message #197064]
|
Tue, 23 September 2008 19:24
|
|
Headrock |
|
Messages:1760
Registered:March 2006 Location: Jerusalem |
|
|
Quote:Perhaps vehicles? Or the robot?
I don't think so, because checks for robots and vehicles look very different. I omitted many such checks from the formula above, btw - Robots and vehicles have special rules in Autoresolve. So do creatures. But as you can see the formula was complicated enough already, didn't want to overload it. So, still no idea what an EPC is.
Quote:A summary of the core insights would be helpful for most people, I'd warrant.
Yes, I was going to do that, but forgot...
Quote:Or that apparenty, doctor mercs make great defenders because of their usually high wisdom and medicine.
Yes, they have an easier time avoiding bullets. However they also attract fire, so expect at least some damage, especially if outnumbered.
Quote:So what I glean from all you've done here is that a great deal of things I simply assumed would be factored into autoresolve are not factored at all. You've stated that attachments like lasers and scopes are not considered. Range is not considered. (!!!) There is no point to equipping training cadre (mercs stationed with militia to keep them maxed with training after attrition) with any sort of weapon aside from whatever weapon does the largest amount of damage with a single shot at point blank range; meaning forget (standard) machineguns or sniper rifles, go for shotguns. That plus, give them massive armor and train up their strength and medical skill if either of those is easier to train than marksmanship. Merc specialty skills are not considered. Don't bother giving them better vision gear or camo. Don't bother pre-stationing them on rooftops. Don't bother building minefields unless one intends to manually fight the battle (actually that one I never assumed otherwise).
All correct, but not entirely.
A) If you can get one, an anti-material sniper rifle would probably be best. Especially because it can use armor-piercing ammo. I think this would have better results, as ammo still plays a roll (in penetrating armor!).
B) What if you're overwhelmed in auto-resolve and want to play the battle hands-on? I say always keep your people equipped if you can... Just in case.
Quote:
I for one would sure appreciate a rebuild of the autoresolve system such that that many of these other factors were in fact considered.
I completely agree. Then again, I would argue that most of the fun I derive from the game is through the tactical game.
Report message to a moderator
|
|
|
|
|
|
Re: "How does it work?" Part 6: Auto-Resolve[message #197086]
|
Tue, 23 September 2008 21:04
|
|
Headrock |
|
Messages:1760
Registered:March 2006 Location: Jerusalem |
|
|
Quote:I on the other hand enjoy the collecting and equipping part of the game perhaps disproportionately compared to most.
Let's just say that beyond my takeover of Drassen, all items I find are usually hoarded and meticulously collected in caches all around the map. Any new merc, even a ridiculously weak one, will immediately be outfitted with the best equipment I've so far seen. So you're not alone there. And yet, all of this eventually goes towards tactical combat, where I expect every merc to perform to the best of his abilities. I leave autoresolve strictly for militia, if I can help it. Fortunately, as I mentioned in the article, I made a small change to one of the calculations and now have the benefit of much better (rather, more accurate) results in autoresolve battles, so I can just zoom through them and (provided my militia were well prepared) worry little about the outcome. So AutoResolve, for me, is just a method to quickly get things out of the way that shouldn't interfere with my manual combat. I think most people here think the same.
Quote:For me, learning that most equipment choices have little to no effect in autoresolve is quite a bit of a letdown.
Well, I've already published several ideas about improving autoresolve combat, unfortunately there was no one to pick up those ideas, and they generated a poor public response overall. It's hardly surprising, because as I said, autoresolve is, for most people, no substitute for hands-on tactical combat. However, now that I have some familiarity with the formula, I might be able to adjust it in several ways. Range should be the easiest, even if I do choose to base it on random factors. Like having the program track imaginary range values and apply gun range to these. Theoretically, it shouldn't be difficult to create a whole battle system, albeit a simple one, to take into account at least twice as many factors as AutoResolve does today. I have some old turn-based battle design ideas in my drawer that should be simple to implement here. But again, the amount of work should always equal the interest in the project, and I doubt there is much demand for anything more than range and, perhaps, first aid.
Report message to a moderator
|
|
|
|
|
|
Re: "How does it work?" Part 6: Auto-Resolve[message #197102]
|
Tue, 23 September 2008 23:43
|
|
Action |
|
Messages:11
Registered:February 2001 |
|
|
Just a guess but:
EPC = Escorted player character
It's checking so that Angel's prostitute sister and Mary Kulba don't start throwing punches in combat.
And it's boosting their defense because they're just hiding.
edit:
haha, i probably should have checked if there were new posts before replying, reading the OP took a while
[Updated on: Wed, 24 September 2008 01:20] by Moderator Report message to a moderator
|
Private
|
|
|
|
Re: "How does it work?" Part 6: Auto-Resolve[message #197342]
|
Fri, 26 September 2008 03:24
|
|
Panpiper |
|
Messages:41
Registered:February 2007 Location: Montreal, Canada |
|
|
Headrock...the amount of work should always equal the interest in the project, and I doubt there is much demand for anything more than range and, perhaps, first aid.
I would agree with you but stipulate that in my opinion the only interest that 'really' counts is the interest of the coder. I think it would be most unreasonable to expect a coder to spend time coding something they are 'not' interested in. It would be also unreasonable to expect a coder to 'not' code something they 'are' interested in. In short, a coder should consider the degree of interest in a coding project, by the non-coding public only, if they have an equal personal interest in two or more possible coding projects and they are trying to choose between them.
So if this is something 'you' would like to do, above any other project you might have in mind, by all means go for it!
Maybe it would be possible to abstract combat bonuses for having certain types of equipment in inventory. That would be fairly easy to program. Camo kits available, small bonus to defence, scope/laser in inventory, bonus to offense, etc..
Report message to a moderator
|
Corporal
|
|
|
|
|
|
|
Goto Forum:
Current Time: Mon Dec 02 00:32:16 GMT+2 2024
Total time taken to generate the page: 0.01221 seconds
|