Skip to content

Instantly share code, notes, and snippets.

@friscojosh
Last active April 8, 2020 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save friscojosh/d76b545fd705cc804f2fbc040b987be9 to your computer and use it in GitHub Desktop.
Save friscojosh/d76b545fd705cc804f2fbc040b987be9 to your computer and use it in GitHub Desktop.
Shadow Vulnerability simulation
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