One other question arises from the function **is.scatter.hit** is: what is the probability profile of catching a model with blast damage when the shot is short? I made a tweak to **is.scatter.hit** so that it would work on a vector of distance values. This means that I can reuse the code from my last post to calculate the probability of hitting at each position over the weapon’s range.

> source("WMTools/R/isscatterhit.R") > is.scatter.hit function(weapon, short = 0, base = 30, max = 6, dice = sample(1:6, size = 2, replace = TRUE)) { rad <- 0.5 * weapon$stats["AOE"] if (is.null(base) || is.na(base) || length(base) != 1) { stop("base should be length 1") } radBase <- 0.5 * base / 25.4 isHit <- FALSE if (!is.na(rad)) { if (length(dice) < 2) { stop("insufficient dice provided, but two required") } if (!all(dice[1:2] %in% 1:6)) { stop("expecting D6 result, but instead: ", paste(dice, collapse = ", ")) } moves <- dice[1] if (moves > max) { moves <- max } x <- cos(pi/6) y <- sin(pi/6) # system is symmetrical, so only solve for one half of x dir <- switch(dice[2], c(0, 1), # straight forward c(x, y), # forward c(x, -y), # backward c(0, -1), # straight backward c(x, -y), # backward c(x, y)) # forward sep <- rep(0, times = length(short)) sep[short != 0] <- short[short != 0] + radBase dmove <- sqrt((sep - moves * dir[2])^2 + (moves * dir[1])^2) isHit <- unname(abs(dmove) < radBase + rad) } return(isHit) } > # rerun the unit tests to make sure

> # that the changes have not broken anything! > source("WMTools/test/test.isscatterhit.R") isscatterhit is okay

It is now just a small change to collect the extra dimension of calculations.

> # values to test > diams <- c(30, 40, 50, 120) > aoes <- c(3, 4, 5) > shorts <- seq(from = 0.01, to = 10, by = 0.01) > > # objects to collect combinations > probs <- array(NA, + dim = c(length(diams), length(aoes), length(shorts)), + dimnames = list(diams, aoes, shorts)) > # direction is important this time > combs <- expand.grid(dist = 1:6, dir = 1:6) > out <- matrix(NA, nrow = nrow(combs), ncol = length(shorts)) > > # as before, except we create two dimensional summaries > for (k in seq_along(aoes)) { + for (j in seq_along(diams)) { + for (i in seq_len(nrow(out))) { + out[i, ] <- is.scatter.hit(list(stats = c(AOE = aoes[k])), + short = shorts, base = diams[j],

+ dice = unlist(combs[i, ])) } + + probs[j, k, ] <- colSums(out) / nrow(out) + }}

We can plot these probabilities by base size to make them easier to compare.

> for (d in seq_along(diams)) { + plot(range(shorts), range(probs), type = "n", + xlab = rep(c("", "Short (inches)"), each = 2)[d], + ylab = rep(c("Probability", ""), times = 2)[d]) + # add lines, extracting for each base diameter + matlines(shorts, + t(apply(probs, 3, function(x) { x[d, , drop = TRUE] })), + lty = 1, col = c("#EC7720CC", "#2A63E2CC", "#22EE22CC")) + legend("topright", paste(diams[d], "mm"), bty = "n") + }

These plots show the probability of a hit for 3 inch (orange), 4 inch (blue) and 5 inch (green) area of effects. The calculations were made every 0.01 inches, so these flat sections actually represent sections where small differences in the distance from the target hit on the same rolls.

There are some interesting quirks of these profiles. Most notably, a 5 inch template is more likely to scatter when between 0.65 and 1.03 inches short of its target than when closer.

The wiggly tail represents where the template is far enough from the target that only a one on the direction die causes a hit, with a high enough distance die. These plots are similar since the distance measurement is made to the edge of the target model’s base, rather than the centre of the model.

These profiles are exact analytical results. However, I will use **is.scatter.hit** for simulating the effectiveness of warjacks and warbeasts ranged attacks at different ranges, since the number of combinations of strategies and dice outcomes is very large.