--- title: "DMG versus dndR" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{DMG versus dndR} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` ```{r libs, echo = FALSE, message = FALSE} # devtools::install_github("njlyon0/dndR", force = TRUE) library(dndR); library(ggplot2); library(magrittr); library(dplyr); library(tidyr) ``` ## `dndR` versus DMG Comparisons See below for some comparisons between my functions and the Dungeon Master's Guide statistics they attempt to recapitulate. ### `cr_convert` vs. DMG `cr_convert` is embedded in the `monster_stats` function and is what allows that function to handle both CR and XP inputs. The DMG specifies the XP value of a monster of any CR from 0 to 30 so `cr_convert` uses the formula of that line to avoid querying the table for this conversion. Below is the comparison of the DMG's XP-to-CR curve and the one produced by `cr_convert`. ```{r cr-convert-plot, echo = F, warning = F, error = F, fig.height = 4, fig.width = 4, fig.align = "center"} cr_actual <- data.frame( "cr" = c(0, 0.125, 0.25, 0.5, 1:30), "dmg_xp" = c(0, 25, 50, 100, 200, 450, 700, 1100, 1800, 2300, 2900, 3900, 5000, 5900, 7200, 8400, 10000, 11500, 13000, 15000, 18000, 20000, 22000, 25000, 33000, 41000, 50000, 62000, 75000, 90000, 105000, 120000, 135000, 155000) ) %>% # For each row... dplyr::rowwise() %>% # Calculate XP for a given CR using `cr_convert` dplyr::mutate("calc_xp" = dndR::cr_convert(cr = cr)) %>% # Pivot to long format tidyr::pivot_longer(cols = dplyr::contains('_xp'), names_to = "authority", values_to = "xp") %>% # And clean up the entries of the source column dplyr::mutate(source = base::ifelse(test = (authority == "calc_xp"), yes = "dndR", no = "DMG")) # Creat the plot comparison ggplot(cr_actual, aes(x = cr, y = xp, shape = source)) + geom_point(aes(fill = source), color = 'black', size = 3, alpha = 0.8, pch = rep(x = c(21, 24), times = 34), position = position_dodge(width = 0.5)) + geom_smooth(aes(color = source), method = 'loess', formula = 'y ~ x', se = FALSE) + # geom_smooth(formula = 'y ~ x', method = 'loess', se = F) + labs(x = "Challenge Rating (CR)", y = "Experience Points (XP)") + scale_color_manual(values = c("#c51b7d", "#4d9221")) + scale_fill_manual(values = c("#c51b7d", "#4d9221")) + theme_classic() + theme(legend.position = c(0.2, 0.9), legend.title = element_blank(), axis.text.y = element_text(size = 12), axis.text.x = element_text(size = 14), axis.title = element_text(size = 16)) ``` ### `xp_pool` vs. DMG The DMG specifies the XP threshold *per player* for a given difficulty while my function asks for the *average* player level and the party size. This difference keeps the function streamlined and flexible for parties of any size. If average party level is an integer, the DMG's table for the encounter XP to player level is used. Otherwise, `xp_pool` uses the formula for the line defining the XP-party level curve implicit in the DMG's table. This has the benefit of being able to handle parties where not all players are the same level. Below is a comparison of the DMG's XP-to-party level curve versus the one obtained by `xp_pool`. ```{r xp-pool-plot, echo = F, warning = F, error = F, fig.height = 4, fig.width = 4, fig.align = "center"} xp_df <- data.frame('pc_level' = 1:20, 'easy_xp' = c(25, 50, 75, 125, 250, 300, 350, 450, 550, 600, 800, 1000, 1100, 1250, 1400, 1600, 2000, 2100, 2400, 2800)) %>% # For each row... dplyr::rowwise() %>% # Calculate the XP for that party level using my function dplyr::mutate(calc_xp = dndR::xp_pool(party_level = pc_level, party_size = 1, difficulty = 'easy')) %>% # Rename a column more intuitively dplyr::rename(book_xp = easy_xp) %>% # Pivot longer for ease of plotting tidyr::pivot_longer(cols = -pc_level, names_to = 'calc_method', values_to = 'xp') %>% # Change the entries of the calc_method column dplyr::mutate(authority = base::ifelse(test = (calc_method == "book_xp"), yes = "DMG", no = "dndR")) # Create the plot ggplot2::ggplot(xp_df, aes(x = pc_level, y = xp, shape = authority)) + geom_point(aes(fill = authority), color = 'black', size = 3, alpha = 0.8, pch = rep(x = c(21, 24), times = 20), position = position_dodge(width = 0.5)) + geom_smooth(aes(color = authority), method = 'loess', formula = 'y ~ x', se = FALSE) + labs(x = "Party Level", y = "Experience Points (XP)") + scale_color_manual(values = c("#f46d43", "#74add1")) + scale_fill_manual(values = c("#f46d43", "#74add1")) + theme_classic() + theme(legend.position = c(0.15, 0.9), legend.title = element_blank(), axis.text.y = element_text(angle = 90, hjust = 0.5), axis.text = element_text(size = 14), axis.title = element_text(size = 16)) ```