Last active
April 8, 2020 18:59
-
-
Save friscojosh/d76b545fd705cc804f2fbc040b987be9 to your computer and use it in GitHub Desktop.
Shadow Vulnerability simulation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library("tidyverse") | |
library("tidylog") | |
### Mechanic explantion | |
# When your SB crits, a debuff (shadow vulnerability=SV) is placed on your | |
# target. That debuff increases ALL shadow damage taken by 20% and can last | |
# up to 12 seconds. So SV will actually increase the damage: | |
# | |
# - your shadow dots tick for, | |
# - of your next SB, | |
# - of your drain life, | |
# - of another warlock's SB and shadow dots and drain life, | |
# - of the spells of a shadow priest, | |
# - of a shadow dmg wand | |
# - etc..etc.. | |
# | |
# Now, SV has 4 charges hence the number "4" on the icon on your target. That | |
# means that the debuff will stay on the target until 4 NON-PERIODIC shadow dmg | |
# sources are applied. So dots do NOT consume the charges of SV debuff. Direct | |
# shadow damage will though. So a shadow bolt, a shadow burn, a shadow wand or | |
# a mind blast will consume 1 charge. | |
# | |
# When all 4 charges are consumed by direct shadow damage or 12 seconds pass | |
# without all 4 charges having been consumed, SV ends. | |
# | |
# If the target is hit by a new shadow bolt crit while under the influence of | |
# the debuff, the charges become again 4 and the 12 sec timer is reset and | |
# starts ticking for another 12 seconds. | |
# | |
# I really hope I helped some warlocks figuring how this talent really works. | |
# Source: https://blue.mmo-champion.com/topic/18196-improved-shadow-bolt-explained/ | |
# Original sim code here (by Guybrush?): http://jsfiddle.net/turinpt/rgfdxyr4/ | |
# Player stats | |
crit_hit_expand <- expand.grid(crit = seq(1:50) / 100, hit = c(83:99) / 100) | |
crit <- crit_hit_expand %>% pull(crit) | |
hit <- crit_hit_expand %>% pull(hit) | |
# Shadow Vulnerability counters | |
sv_charges <- 0 | |
sv_uptime <- 0 | |
sv_timeleft <- 0 | |
# Metrics | |
counter <- 0 | |
lock_missed <- 0 | |
is_crit <- 0 | |
sim <- function(crit, hit) { | |
simulations <- 100000 | |
#Player Constants | |
bolt <- 1054.74 | |
sb_casttime <- 2.5 | |
for ( i in seq(1:simulations)) { | |
counter = counter + 1 | |
if (sv_charges > 0) { sv_timeleft = sv_timeleft - sb_casttime } | |
if (sv_timeleft < 0) { sv_charges = 0 } | |
if (sv_charges > 0) { sv_uptime = sv_uptime + sb_casttime } | |
if (runif(1) > hit) { | |
lock_missed = lock_missed + 1 | |
next } | |
if (runif(1) <= crit) { | |
is_crit = is_crit + 1 | |
if (runif(1) <= hit) { | |
sv_charges = 4 | |
sv_timeleft = 12 | |
} | |
else { | |
if (sv_charges > 0) { sv_charges = sv_charges - 1 } | |
} | |
} else { | |
if (sv_charges > 0) { sv_charges = sv_charges - 1 } | |
} | |
} | |
# ISB Uptime. Assume constant, uninterrupted casting and no lag | |
sim_sv_uptime <- sv_uptime / (simulations * sb_casttime) | |
# Simulated crit rate | |
sim_crit <- round(is_crit / simulations, 3) | |
# True crit rate | |
true_crit <- round(crit * hit, 3) | |
# Roll up metrics for each run of the simulation into a list. | |
sim_vars <- list(sim_sv_uptime = sim_sv_uptime, hit = hit, crit = crit, | |
sim_crit = sim_crit, true_crit = true_crit) | |
} | |
# Map each of the 850 hit and crit values to the sim function | |
sim_results <- pmap(list(crit, hit), sim) %>% | |
map_df(as_tibble) %>% | |
mutate(sim_sv_uptime = round(sim_sv_uptime, 3), | |
quartic_predict = 1 - (1 - crit * hit) ^ 4) | |
# Create a polynomial model to fit our simulation results. | |
model <- lm(data = sim_results, sim_sv_uptime ~ poly(crit * hit, 2, raw = TRUE)) | |
summary(model) | |
confint(model, level=0.95) | |
# Add the model predict to the dataframe for plotting | |
sim_results$predict <- round(predict(model, newdata = sim_results), 3) | |
# Plot 1 | |
sim_results %>% | |
ggplot(aes(x = true_crit, y = predict)) + | |
geom_point(color = "red") + | |
geom_point(aes(x = true_crit, y = quartic_predict)) + | |
theme_minimal() + | |
scale_y_continuous(limits = c(0,1), labels = scales::percent) + | |
scale_x_continuous(limits = c(0,0.5), labels = scales::percent) + | |
labs(x = "True Crit (Hit chance * Critical hit chance)", y = "Estimated Shadow Vulnerability uptime %", | |
title = "Simulations predict lower uptime vs. quartic equation", | |
subtitle = "Formula: 1 - (1 - crit * hit) ^ 4") | |
# Plot 2 | |
sim_results %>% | |
ggplot(aes(x = crit, y = sim_sv_uptime, group = hit, color = as.factor(hit))) + | |
geom_smooth() + | |
theme_minimal() + | |
scale_y_continuous(limits = c(0,1), labels = scales::percent) + | |
scale_x_continuous(limits = c(0,0.5), labels = scales::percent) + | |
labs(x = "Critical hit chance", y = "Shadow Vulnerability uptime %", | |
title = "Shadow Vulnerability uptime by critical hit chance.", subtitle = "Each curve is the simulated SV uptime by crit for a given hit chance.", color = "Hit chance") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment