Skip to content

Instantly share code, notes, and snippets.

@ygshi ygshi/stun.py
Last active Jun 12, 2019

Embed
What would you like to do?
import numpy as np
import random as rd
#settings-----------
#damage and stun settings---------------
mh_hit_damage_all_types=[221027, 709614] #read from PoB; 2 integers.
mh_hit_damage_physical=[221027,709614] #read from PoB; 2 integers.
mh_att_per_second=2.98 #read from PoB; decimal number.
stun_threshold_reduction=131 #read from tooltip; integer.
stun_duration_modifier=316 #read from tooltip; integer.
double_stun_duration=20 #I don't know where to read from; integer.
#double damage, Ruthless and Multistrike settings-----------
enable_double_damage=1 #set to 1 if there's chance for double damage.
double_damage_chance=72 #set double damage chance; integer.
enable_ruthless=1 #set to 1 if Ruthless gem used.
ruthless_multi=123 #set ruthless multiplier; 123 should be default; integer.
enable_multistrike=0 #set to 1 if Multistrike gem used.
multi_repeat_1=50 #set multistrike 1st repeat multiplier; 50 should be default; integer.
multi_repeat_2=99 #set multistrike 2nd repeat multiplier; 99 should be default; integer.
#crit settings---------------
enable_crit=0 #set to 1 if Resolute Technique isn't picked.
if_lucky=1 #set to 1 if crit chance is lucky.
hit_chance=82 #read from PoB; integer.
crit_chance=100.00 #read from PoB; not the effective crit chance; decimal number.
crit_multiplier=665 #read from PoB; integer.
#curse settings--------------
enable_warlords_mark=0 #set to 1 if Warlords Mark used.
enable_temporal_chains=0 #set to 1 if Temporal Chains used.
curse_effectiveness=0 #set increased curse effectiveness; integer.
#cull settings---------------
enable_slayer_cull=0 #set to 1 if Slayer ascendancy Bane of Legends picked.
enable_cull=1 #set to 1 if Skull Cracking passive notable picked or have other sources of culling.
#PoB correction settings----
enable_ryslatha=1 #set to 1 if Ryslatha's Coil used.
ryslatha_max=40 #more maximum damage from your Ryslatha; integer.
ryslatha_min=30 #less minimum damage from your Ryslatha; integer.
if_heavy_strike=1 #set to 1 if Heavy Strike is used.
if_slayer_impact=1 #set to 1 if Slayer's Impact is picked.
if_pride=1 #set to 1 if Pride is used;
mh_eff_dps_mod=1.67 #read from PoB; decimal number.
#other settings--------------
simulation_time=3600 #set simulated time in unit of seconds; integer.
#end of settings-------------
def load():
global atkmin,atkmlt,phys_bonus,atkint,effhp,dschns,curefft,durm,cull_factor,ruthc,multic,time
atkmin=np.float(mh_hit_damage_all_types[0])/1000.
atkmax=np.float(mh_hit_damage_all_types[1])/1000.
if (enable_ryslatha == 1):
atkmin = atkmin * (1. - np.float(ryslatha_min) / 100.)
atkmax = atkmax * (1. + np.float(ryslatha_max) / 100.)
atkmlt=1000.*atkmin/(atkmax-atkmin)
if (if_heavy_strike == 1):
if (enable_double_damage == 1):
atkmin = atkmin * (120. + np.float(double_damage_chance)) / (100. + np.float(double_damage_chance))
if (if_slayer_impact == 1):
atkmin = atkmin * 1.15
if (if_pride == 1):
atkmin = atkmin * 1.39 * (mh_eff_dps_mod - 0.19) / mh_eff_dps_mod
physmin=np.float(mh_hit_damage_physical[0])/1000.
physmax=np.float(mh_hit_damage_physical[1])/1000.
phys_bonus = 1. + 0.25 * (physmin + physmax) / (atkmin + atkmax)
atkint=np.int(1000./np.float(mh_att_per_second))
tstr=np.float(stun_threshold_reduction)
rstr=1.-((tstr-75.)/(tstr-50.))
effhp=boss_life*0.25*rstr
dschns=double_stun_duration
curefft = (100. + np.float(curse_effectiveness)) * 0.01 * 0.34
tsdm=np.float(stun_duration_modifier)
if (enable_warlords_mark == 1):
tsdm = tsdm + 30. * curefft
durm=1.+tsdm/100.
if (enable_temporal_chains == 1):
durm = durm * (1. + 0.14 * curefft)
cull_factor = 1.
if (enable_cull == 1):
cull_factor = 0.9
if (enable_slayer_cull == 1):
cull_factor = 0.8
ruthc=1
multic=1
time=[]
for i in range(0, (simulation_time*1000)):
time.append(i)
def doubledf():
if (enable_double_damage == 1):
if (rd.randint(1, 100) <= double_damage_chance):
return 2.
else:
return 1.
else:
return 1.
def critf():
if (enable_crit == 1):
if (rd.randint(1,100) > hit_chance):
return 0.
if (rd.randint(1,100) > hit_chance):
return 1.
temp = np.float(crit_multiplier) / 100.
if (rd.randint(1,10000) <= np.float(crit_chance) * 100.):
return temp
if (if_lucky == 1) and (rd.randint(1,10000) <= np.float(crit_chance) * 100.):
return temp
return 1.
else:
return 1.
def atkdmg():
temp = atkmin * (1. + np.float(rd.randint(0, 1000)) / atkmlt)
temp = temp * doubledf() * critf()
if (enable_ruthless == 1):
temp = temp * 3. / (3. + np.float(ruthless_multi) / 100.)
if (enable_double_damage == 1):
temp = temp / (1. + np.float(double_damage_chance) / 100.)
if (enable_multistrike == 1):
temp = temp * 300. / (300. + np.float(multi_repeat_1) + np.float(multi_repeat_2))
return temp
def ruthl():
global ruthc
if (enable_ruthless == 1):
temp = (1. + np.float(ruthless_multi) / 100.)
if (enable_multistrike == 1):
if (ruthc < 7):
ruthc = ruthc + 1
return 1.
if (ruthc >= 7):
if (ruthc == 9):
ruthc = 1
return temp
ruthc = ruthc + 1
return temp
if (ruthc < 3):
ruthc = ruthc + 1
return 1.
if (ruthc == 3):
ruthc = 1
return temp
else:
return 1.
def multim():
global multic
if (enable_multistrike == 1):
if (multic == 1):
multic = multic + 1
return 1.
if (multic == 2):
multic = multic + 1
return (100.+np.float(multi_repeat_1))/100.
if (multic == 3):
multic = 1
return (100.+np.float(multi_repeat_2))/100.
return 1.
def ifstun(totdmg):
stunch = np.int(20000. * totdmg * phys_bonus / effhp)
if (enable_warlords_mark == 1):
stunch = stunch + 1000. * curefft
if (stunch < 2000):
return 0
if (rd.randint(1, 10000) > stunch):
return 0
else:
return 1
def stundur():
ruthlim=3
if (enable_multistrike == 1):
ruthlim=7
if (ruthc < ruthlim):
if (rd.randint(1, 100) <= dschns):
return np.int(700 * durm)
else:
return np.int(350 * durm)
if (ruthc >= ruthlim):
if (rd.randint(1, 100) <= dschns):
return np.int(1600 * durm)
else:
return np.int(800 * durm)
def calc():
load()
record=[]
dmgdone=0
atkcnt=0
stunrt=0
for i in time:
if (i == atkint * atkcnt):
atkcnt = atkcnt + 1
totdmg = atkdmg() * ruthl() * multim()
dmgdone = dmgdone + totdmg
if (ifstun(totdmg) == 1):
if (stundur() > stunrt):
stunrt = stundur()
if (stunrt > 0):
record.append(1)
stunrt = stunrt - 1
else:
record.append(0)
return (record, dmgdone)
def analyze():
somearray=[[],0]
somearray=calc()
arraycount=0
firstone=-1
locks=[0]
nlock=-1
prev=0
lockchance=0
for i in somearray[0]:
if (i == 1) and (firstone == -1):
firstone = arraycount
if (i == 1) and (prev == 0):
nlock = nlock + 1
locks.append(0)
if (i == 1):
locks[nlock] = locks[nlock] + 1
arraycount = arraycount + 1
prev = i
uptime = np.float(np.sum(locks)) / np.float(simulation_time * 1000. - firstone)
dmgneeded = boss_life * cull_factor * phase_factor
if (phase_factor == 25):
dmgneeded = boss_life * 75. / 100.
killtime = np.int(dmgneeded / somearray[1] * simulation_time * 1000)
for i in locks:
if (i >= killtime):
lockchance = lockchance + i - killtime + 1
lockchance = np.float(lockchance) / np.float(simulation_time * 1000 - firstone - killtime)
return (uptime, lockchance)
def fight(para1, para2, boss_name):
global boss_life, phase_factor
boss_life=para1
phase_factor=para2
answer=[0,0]
answer=analyze()
print boss_name
print 'Percentage of stun up-time', "%.2f" %(answer[0]*100.), '%'
print 'Chance of perfect stun-locking', "%.2f" %(answer[1]*100.), '%'
print ''
def main():
print 'Start simulating boss fights with total duration of', simulation_time, 'seconds. Please be patient.'
print ''
fight(2886., 1., "T15")
fight(8814., 1./3., "Uber_Izaro")
fight(9538., 1., "Guardian")
fight(15831., 25, "Elder")
fight(20190., 0.5, "Shaper")
print 'End.'
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.