Recent Topics

Ads

[BW]Combat Simulations

Knight of the Blazing Sun, Bright Wizard, Witch Hunter, Warrior Priest
Forum rules
Before posting on this forum, be sure to read the Terms of Use
Your topic MUST start with your class name between hooks (IE : [Shaman] blablabla)
Cimba
Posts: 376

[BW]Combat Simulations

Post#1 » Mon Nov 20, 2017 6:50 pm

Introduction

A couple of months ago I build a simple damage simulator for my Bright Wizard. The original intention was to figure out if the disrupt strikethrough that was available through the Beastlord, Genesis and Conquerer set were worth the loss in critical chance to hit. This was motivated by the fact that there were certain Shamans around that felt utterly unkillable to me.

Fast forward. A couple of weeks ago the defense mechanism were radically changed. Since then there have been a number of threads regarding the state of BWs and Sorcs. When reading these threads the lack of actual, reliable numbers becomes painfully obvious.

In this thread I want to attempt to provide some well founded numbers to the BW/Sorc vs Disrupt problematic.

However this whole disrupt problematic is not that tirival from a single target BW perspective. The reason for that are
  • time dependent debuffs e.g. Willpower, Resistance, Thoughness
  • interdependent procs e.g. Reactionary - Increasing your disrupt chance when defended against an attack
  • Lots of probabilistic elements e.g. defense roll, crit roll, crit damage distribution roll, proc chances
My approach to this problematic is based in the law of large numbers. It basically means that if I can collect enough data than random events (positive and negative) will average themselves out. For this purpose I usually work with 1 or 10 million data sets.

Formulas
This is a summary of my understanding of the combat formulas (that are relevant for this probelm) and therefore the basis for what I wrote into code. I hope it's self explanatory to the people who are interested in this kind of stuff.

Damage Calculation
<Damage> = [<Base Damage> + <Mastery Point Scaling> + <Stat Scale> * (<Intelligence>+<Magic Power>-<Thoughness>)/5]*(1-<Mitigation>)*(1+<Offensive Modifiers>)*(1-<Defensive Modifiers>)

<Crit Damage> = <Damage>*((<random(1.4,1.6)> + <Crit Damage Bonus>) * (1-<Crit Damage Reduction>))

Source
The formula in that post is not entirely correct. On RoR the damage reduction through Thoughness gets also modified by <Offensive Modifiers>. I tested this with Vengeance of Nagarythe. In addition caster dont have a dps contribution.
I haven't tested the way Offensive and Defensive Modifiers interact with each other, yet. This is just an assumption.

<Mitigation> = <Resistance>/(<Defender Level>*0.41902)/100
if Resistance > 672
<Mitigation> = <Resistance>/(<Defender Level>*1.2604 + 26.719)/100

Source

Defense
<Defense Stats> = <Defender Willpower>/((<Defender Level>*7.5+50)*7.5)
<Strikethrough Stats> = <Attacker Intelligence>/((<Attacker Level>*7.5+50)*7.5)

<Defense Modified> = <Defense Stats> + <Sum of disrupt bonus> - <Sum of % based strikethrough)

If <Defense Modified> >= random(0,1+<Strikethrough Stats>) then the spell is defended

Source
That thread is a huge mess. I'm reasonably sure I got it right. But there might be some further iterations to the code that hasn't been disclosed yet.

The Code
The code is written in MATLAB/Octave. Not the most well known language but that what I had on my laptop when I started this months ago. I didn't spent too much time on documentation because quite frankly: I dont believe more than 5 people will look at that code. And these people can contact me if they have questions.
I did verify the code to the best of my abilities and knowledge but that doesnt mean it's 100% accurate or bug free.

The basic concept is that you define a list of spells that will be used in your rotation. Based on that a list of actions is created e.g. when DoT X hits. With this list the actual combat calculations are done. For the combat calculation I use almost execlusivly matrix manipulations. Reason for that is a) that is the strong point of MATLAB b) it's the only way I could think of solving 10 milion or more calculations in parallel.
I'm more or less self taught in everything coding related. So there are probably better ways to do it and a ton of coding standards/conventions I violated. But thats what you got :). Any one my hardware (i5 6600K with 8 GB RAM) the calculations take about
  • 1 million --> 4 seconds
  • 10 million --> 60-90 seconds
  • 1 billion --> MATLAB goes crazy
Octave (open source variant of MATLAB) is a bit slower.


This is the main file that gets executed. The post processing isnt really done yet, but I guess if your capable of running the code you can adjust that to your own likings.
Spoiler:

Code: Select all

close all; clear
tic

GCD = 1.5; % global cooldown in seconds
run = 1E6; % number of calculations

%% Ability List
Spells

rotation = {Ignite;...
            BoilingBloodMod;...
            PyroclasticSurge;...
            Sear;...
            FBB;...
            Nova};

%% Creating a list of actions
a_list = []; time = 0;
for kk=1:size(rotation,1) % looping through the defined rotation
if strcmp(rotation{kk}{2},'DOT') == 1 % dots/debuffs
    a(:,1) = [time, time + rotation{kk}{6} + rotation{kk}{7}, max(time + rotation{kk}{6} + rotation{kk}{7})]';
    a(:,2) = 1; % effect damage
    a(1,2) = 2; % effect start
    a(end,2) = 3; % effect end
    a(:,3) = kk; % spell identifier
    
    time = time + max(GCD,rotation{kk}{6}); % when the next action can be taken
elseif strcmp(rotation{kk}{2},'CH') == 1  % channeled abilities
    a(:,1) = [time + rotation{kk}{6} + rotation{kk}{7}]'; % when ability hits   
    a(:,2) = 1; % effect damage
    a(:,3) = kk; % spell identifier

    time = time + max(GCD,max(rotation{kk}{7})); % when the next action can be taken
else % direct damage atbilites
    a(:,1) = time + rotation{kk}{6} + rotation{kk}{7}; % when ability hits
    a(:,2) = 1; % effect damage
    a(:,3) = kk; % spell identifier

    time = time + max(GCD,rotation{kk}{6}); % when the next action can be taken
end
a_list = [a_list; a]; % append actions to list
clear a % reset temp variables
end
a_list = sortrows(a_list,1); % sort the actions by time

% attacker and defender stats
Stats

%% Damage Calculation
dmg_fin = []; disp_chk = [];
for kk=1:size(a_list,1) % loop through actions list previously defined
dmg_spell = []; rate_disp = []; % reset temp variable
% recalculate relevant stats for each action
thg_tot(1:run,1)= thg_base - thg_debuff + thg_buff; % thoughness
int_tot(1:run,1)= int_base - int_debuff + int_buff; % intelligence
wp_tot(1:run,1) = wp_base - wp_debuff + wp_buff; % willpower
init_tot(1:run,1) = init_base - init_debuff + init_buff; % initiative
OffStatDelta = int_tot + mgcpower - thg_tot; % delta between int,thg and magic power

corp_tot(1:run,1) = corp_base - corp_debuff + corp_buff; % corporal resistance
spirit_tot(1:run,1) = spirit_base - spirit_debuff + spirit_buff; % spirit resistance
elem_tot(1:run,1) = elem_base - elem_debuff + elem_buff; % elemental resistance
armor_tot(1:run,1) = armor_base - armor_debuff + armor_buff; % armor

% Crit rate
crit_stats = (7.5.*lvl_def+50)./init_tot./10; % chance to be critically hit
crit_tot = crit_stats+crit_subsum; % adding crit from gear, renown etc

% Mitigation
% Softcap is at 672 resistance. 
corp_scap = corp_tot > 672;
corp_mitg = (corp_tot./(lvl_def.*0.41902)./100).*(1-corp_scap)+...
            ((corp_tot./(lvl_def.*1.2604) + 26.719)./100).*corp_scap;

spirit_scap = spirit_tot > 672;
spirit_mitg = (spirit_tot./(lvl_def.*0.41902)./100).*(1-spirit_scap)+...
            ((spirit_tot./(lvl_def.*1.2604) + 26.719)./100).*spirit_scap;

elem_scap = spirit_tot > 672;
elem_mitg = (elem_tot./(lvl_def.*0.41902)./100).*(1-elem_scap)+...
            ((elem_tot./(lvl_def.*1.2604) + 26.719)./100).*elem_scap;
            
    if a_list(kk,2) == 1 % damage calculation
        Defense
        % Base damage
        dmg_init = round((rotation{a_list(kk,3)}{3} + rotation{a_list(kk,3)}{4}.*OffStatDelta./5).*(1-evalin('base',rotation{a_list(kk,3)}{5}))).*(1+OffMod).*(1-DefMod);
        % Crit damage
        rate_crit = rand(run,size(dmg_init,2)) <= crit_tot; % Crit = 1, No crit = 0
        % base damage gets modified in case of crits
        dmg_crit = rate_crit.*round(dmg_init.*(0.2*rand(run,1)+1.4+CritMod));

        if strcmp(rotation{a_list(kk,3)}{2},'DOT') == 1 
            % If dot damage --> multiply the damage calculation with initial
            % defense results
            dmg_spell = max(dmg_crit,dmg_init).*rate_disp.*dot_disp_init(:,a_list(kk,3));   
        else
            % for direct damage add proc damage        
            Procs
            dmg_spell = (max(dmg_crit,dmg_init)+dmg_proc).*rate_disp;
        end
    elseif a_list(kk,2) == 2 % effect start
        Defense
        dot_disp_init(:,a_list(kk,3))= rate_disp; % Disrupt = 0, No Disrupt = 1
        evalin('base', rotation{a_list(kk,3)}{8});       
    elseif a_list(kk,2) == 3 % effect end
        evalin('base', rotation{a_list(kk,3)}{9});  
    end

% final damage matrix
dmg_fin = [dmg_fin, dmg_spell];

% Various checks to verify code 
disp_chk = [disp_chk,rate_disp];

clear dmg_init rnd_crit rate_crit dmg_crit rate_FOR dmg_proc % clear tmp variables
fprintf('Calculation is %d %% done. \n',round(kk/size(a_list,1)*100))
end


%% Post Processing
close all
time_dmg = a_list(a_list(:,2) == 1); % 

% Disrupt development
disp_effective = 1-sum(disp_chk)/run; % effective disrupt rate
figure('Name', 'Disrupt Development')
plot(disp_effective)
grid on


% Damage development
avg_per_tick = mean(dmg_fin); % Average damage of each damage instance
avg_dmg_rota = sum(avg_per_tick); % Average rotation damage
dmg_rota = sum(dmg_fin(:,3:size(dmg_fin,2)),2); % Relevant rotation damage


var1 = 1; ii = 1;
while var1 ~= 0
dmg_dist(ii,1) = 500*(ii-1);
dmg_dist(ii,2) =  500*(ii); 
dmg_dist(ii,3) = sum(dmg_rota(:) >= 500*(ii-1) & dmg_rota(:) < 500*(ii))/run*100;
var1 = dmg_dist(ii,3);
ii = ii+1;
end

figure
plot(dmg_dist(:,3))
grid on


toc
kill_rate = sum(dmg_rota(:) >= wou_base*10)/run*100;

Spell Database. Well you need some input values if you want to get accurate results. Here is the test database I used to verify my code.
Spoiler:

Code: Select all

%% Spells
% Name Type BaseDMG StatScale Resistance CastTime TimeToDamage 
ProjectileSpeed = 1.3;
Nova = {'Nova' 'DD' 393 1.5 'elem_mitg' 0 0 };
PyroclasticSurge = {'Pyroclastic Surge' 'DD' 332 2 'corp_mitg' 2 ProjectileSpeed};
Sear = {'Sear' 'DD' 322 1.5 'elem_mitg' 1 0};
Fireball = {'Fireball' 'DD' 572 3 'elem_mitg' 3 ProjectileSpeed};
FBB = {'Fireball Barrage' 'CH' 429 0.75 'elem_mitg' 0 [0 1.5]};

% Dots, last arguments are the effects they apply and the restoration to base value
Ignite = {'Ignite' 'DOT' 156 0.5 'elem_mitg' 0 [3 6 9] '' ''};
BoilingBloodMod = {'Fuel to the Fire' 'DOT' 608 1.5 'corp_mitg' 0 5 'wp_debuff = 306.*rate_disp;' 'wp_debuff(1:run,1) = 0;'};
BoilingBlood = {'Boiling Blood' 'DOT' 389 1.5 'corp_mitg' 0 10 'wp_debuff = 102.*rate_disp;' 'wp_debuff(1:run,1) = 0;'};
SlowBoil = {'Slow Boil' 'DOT' 215 1.5 'corp_mitg' 0 5 '' ''};
 
% Procs
% Last argument is the proc rate
FlamesOfRhuin = {'Flames of Rhuin' 'Proc' 215 0 'elem_mitg' 0 0 0.25};
FunnelPower = {'Funnel Power' 'Proc' 145 0 'corp_mitg' 0 0 1};
You also need some stats for your attacker and defender. These are for my BW and a full conq AM with winds of impervious set + beastlord.
Spoiler:

Code: Select all

%% Stats
lvl_att = 40; % rank defender
lvl_def = 40; % rank attacker
% Attacker Stats
int_base = 1050; % intelligance
mgcpower = 110; % magic power
OffMod = 0; % offensive modifiers e.g. Obessesive Focus (sorc)

% Defender Stats
wp_base = 496; % willpower
thg_base = 287; % thoughness
init_base = 231; % initiative
wou_base = 665; % wounds
DefMod = 0; % defensive modifiers e.g. guard

% Defender Mitigation
spirit_base = 571;
elem_base = 580;
corp_base = 564;
armor_base = 2390;

% Buffs
thg_buff(1:run,1) = 0;
int_buff(1:run,1) = 0; 
wp_buff(1:run,1) = 80; % willpower buffs e.g. pot = 80
wou_buff(1:run,1) = 0; 
init_buff(1:run,1) = 0;
corp_buff(1:run,1) = 0;
elem_buff(1:run,1) = 252;
spirit_buff(1:run,1) = 252;
armor_buff(1:run,1) = 252;

% Debuffs
thg_debuff(1:run,1) = 0; 
int_debuff(1:run,1) = 0; 
wp_debuff(1:run,1) = 0;
wou_debuff(1:run,1) = 0; 
init_debuff(1:run,1) = 0;
corp_debuff(1:run,1) = 0;
elem_debuff(1:run,1) = 0;
spirit_debuff(1:run,1) = 0;
armor_debuff(1:run,1) = 0;

% Disrupt 
disp_gear = 0.02; % percentages based disrupt from gear
disp_renown = 0.18; % percentages based disrupt from renown (Deft Defender)
disp_tactics = 0.00; % % percentages based disrupt from tactics

disp_subsum = disp_gear + disp_renown + disp_tactics; % subsum without skill gain
disp_skill(1:run,1) = 0; % % percentages based disrupt from skill, seperate because can change during fight

strkthr_gear = 0.07; % disrupt strikethrough from gear
strkthr_tactics = 0; % disrupt strikethrough from tactics
strkthr_subsum = strkthr_gear + strkthr_tactics; % disrupt strikethrough sum

% Crit
crit_gear = 0.12;
crit_renown = 0.14;
crit_class = 0.35;
crit_tactics = 0.00;
anti_crit_gear = 0.00;
anti_crit_renown = 0; % futile strikes values are 3 8 15 24
crit_subsum = crit_class + crit_gear + crit_renown + crit_tactics...
              -anti_crit_gear - anti_crit_renown;
CritMod = 1; % critical damage modifier e.g. BW/sorc = 1 at 100 mechanic

I'm not entirely sure if my interpreation of the defensive mechanism is correct so I wrote it into a seperate file to modifier it easier.
Spoiler:

Code: Select all

%%Defense
disp_stats = wp_tot./((lvl_def.*7.5+50).* 7.5); % defense generated by stats
strkthr_stats = int_tot./((lvl_att.*7.5+50).* 7.5); % strikethrough generated by stats

% defensive gets modified by percentage based modifiers
def_mod = disp_stats + disp_subsum + disp_skill - strkthr_subsum;

% Defense roll, not 100% sure if this is correct. The thread mentioning this is a mess
rate_disp = rand(run,1).*(1+strkthr_stats) >= def_mod; % Disrupt = 0, No Disrupt = 1

%% Defense Procs
% Defensive Proc on AM conquerer set
disp_skill(rate_disp == 0 & rand(run,1) <= 0.25) = 0.1;

%% Postprocessing tools
% Write name of ability into vector
def_ab_list{end+1} =rotation{a_list(kk,3)}{1};

File for offensive Procs. Since it want to test out different procs I also wrote into a seperate file. So I dont have to **** up my main file.
Spoiler:

Code: Select all

%% Procs
% Calculating Proc damage on direct damage abilities
dmg_FOR = round(FlamesOfRhuin{3}.*(1+OffMod).*(1-DefMod).*evalin('base',FlamesOfRhuin{5}));
dmg_FP =  round(FunnelPower{3}.*(1+OffMod).*(1-DefMod).*evalin('base',FunnelPower{5}));
rate_FOR = rand(run,1) <= FlamesOfRhuin{8}; % Proc = 1, No Proc = 0
dmg_proc = rate_FOR.*dmg_FOR + dmg_FP;

% Domination proc on BW conquerer Set
thoughness_debuff(rand(run,1) <= 0.25) = 100;
So why am I posting this here? Well I hope at least one person looks at the code, catches an error and tells me :). If no-one finds any bugs I will start to run some studies in the future and hopefully provide some well founded numbers.

Preliminary Results
Maybe as a preliminary result I can show you the average disrupt development of my BW with 7% disrupt strike through at 1050 Intelligence. Against a full conquerer AM with 576 Willpower + Max Deft Defender.

Image
Picture Link
The Y-Axis shows the average disrupt rate after 1 million rotations. The X-Axis shows the disrupt checks each ability has to perform.
As can be seen in the picture the 306 willpower debuff on the boiling blood reduces the average disrupt rate by about 6%. However due to Reactionary Porc on the set the average disrupt rate continues to increase. The defense procs results for this rotation in a final increase of about 3% effective disrupt.

Edit: 2017-11-21: Changed the wording a bit; Added Sources and some Comments to the combat formulas section
Last edited by Cimba on Tue Nov 21, 2017 10:23 am, edited 3 times in total.

Ads
Cimba
Posts: 376

Re: [BW]Combat Simulations

Post#2 » Mon Nov 20, 2017 6:50 pm

Hopefully reserved for more results. Soon.

User avatar
Haojin
Posts: 1062

Re: [BW]Combat Simulations

Post#3 » Mon Nov 20, 2017 7:11 pm

at last some legit stuff. gonna check tomorrow.
Guildmaster of Phalanx

K8P - Karak Norn

User avatar
keldz
Posts: 31

Re: [BW]Combat Simulations

Post#4 » Mon Nov 20, 2017 7:28 pm

Very good initiative
Izna - CCM

"Cuir Cuir Moustache" - small scale french PvP guild

User avatar
Delenot
Posts: 104

Re: [BW]Combat Simulations

Post#5 » Mon Nov 20, 2017 7:31 pm

Thank you Cimba!

Jbz
Posts: 48

Re: [BW]Combat Simulations

Post#6 » Mon Nov 20, 2017 7:41 pm

If you need another body for testing message me.

Cimba
Posts: 376

Re: [BW]Combat Simulations

Post#7 » Wed Nov 22, 2017 1:09 pm

I thought about starting the result section of my little simulation with something that doesnt actually require the code described. Instead I want to start of with providing some graphs about the disrupt rates that we can currently encounter.

The effetive disrupt rate is to the best of my knowledge

<Disrupt Rate> = [<Defender Willpower>/((<Defender Level>*7.5+50)*7.5) + <Sum of disrupt bonus> - <Sum of % based strikethrough>]/[1+<Attacker Intelligence>/((<Attacker Level>*7.5+50)*7.5)]

When we set the attacker and defender level to 40 we can express this disrupt rate as a function of Willpower and Intelligence (and the additional values of disrupt and strikethrough). This gives us a surface graph with the disrupt rates.

Without any percentage based modifiers it looks like this
Image
I think the average base willpower is somewhere around 150. This would provide about 5% against a 1050 intelligence attacker. When you soft cap willpower you would get about 30% disrupt against the same attacker.

A bit more realistic is that the attacker has some disrupt strike through and the defender at least some addtional disrupt from gear.
Image
These percentage based modifiers act basically as offsets of surfrace shown in the first figure. With the addition that the disrupt rate (hopefully) cant go into negatives.

Now you can add all sorts of disrupt bonuses: Deft Defender, Hold The Line, Shielding Anger. This tops probably out with the Black Guard who can comfortably sit at 90% disrupt due to disrupt tactics and high value willpower buffs.
Image

Link to the pictures

Additional pictures
Spoiler:
Image
Image
Image

User avatar
Flamers
Posts: 27

Re: [BW]Combat Simulations

Post#8 » Wed Nov 22, 2017 3:43 pm

Thank you for the time and effort you put into this

Ads
User avatar
Njaguar
Posts: 24

Re: [BW]Combat Simulations

Post#9 » Wed Nov 22, 2017 4:37 pm

Image

Cimba
Posts: 376

Re: [BW]Combat Simulations

Post#10 » Fri Nov 24, 2017 11:43 am

It was pointed out that you cant really see too much on the 3D plots. Which unforuntately appears to be true. So here a simple 2D plot of the disrupt gain rate with willpower. The different lines in the plot represent the amout of disrupt you got through items, tactics, deft defender etc.

Image

For bigger picture, click here.

Who is online

Users browsing this forum: No registered users and 11 guests