Skip to content

Commit

Permalink
Tweaked xp_pool to use the DMG's party level-to-XP data if average …
Browse files Browse the repository at this point in the history
…party level is an integer but otherwise calculate the relevant XP manually
  • Loading branch information
njlyon0 committed May 3, 2024
1 parent 27a3fe0 commit 082d3d6
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 16 deletions.
47 changes: 33 additions & 14 deletions R/xp_pool.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#'
#' @description Returns the total XP (experience points) of all creatures that would make an encounter the specified level of difficulty for a party of the supplied level. This 'pool' can be used by a GM (game master) to "purchase" monsters to identify how many a party is likely to be able to handle given their average level. NOTE: this does not take into account creature-specific abilities or traits so care should be taken if a monster has many such traits that modify its difficulty beyond its experience point value.
#'
#' @param party_level (numeric) integer indicating the average party level. If all players are the same level, that level is the average party level
#' @param party_level (numeric) integer indicating the average party level. If all players are the same level, that level is the average party level. Non-integer values are supported but results will be slightly affected
#' @param party_size (numeric) integer indicating how many player characters (PCs) are in the party
#' @param difficulty (character) one of "easy", "medium", "hard", or "deadly" for the desired difficulty of the encounter.
#'
Expand All @@ -16,31 +16,50 @@
xp_pool <- function(party_level = NULL, party_size = NULL, difficulty = NULL){

# Error out if party_level or difficulty is unspecified
if(base::is.null(party_level) | base::is.null(party_size) | base::is.null(difficulty)) stop("At least one parameter is unspecified. See `?dndR::xp_pool()` for details")
if(base::is.null(party_level) | base::is.null(party_size) | base::is.null(difficulty))
stop("At least one parameter is unspecified. See `?dndR::xp_pool()` for details")

if(base::is.numeric(party_level) != TRUE | base::is.numeric(party_size) != TRUE)
stop("Party level and party size must be a number")

# Error out if too many party levels are provided
if(base::length(party_level) > 1) stop("Too many values provided. What is the *average* level of PCs in the party?")
if(base::length(party_level) > 1)
stop("Too many values provided. What is the *average* level of PCs in the party?")

# Error out if difficulty is not supported
if(!base::tolower(difficulty) %in% c('easy', 'medium', 'hard', 'deadly'))
stop("Unrecognized difficulty level. Please use only one of 'easy', 'medium', 'hard', or 'deadly'")

# Identify the quadratic coefficients
## (Did this manually and got a decent fit for the line)
a <- 8.216374
b <- -26.49122
c <- 43.27485
# If party level is an integer, use the DMG's values
if(is.integer(party_level) == TRUE){

# Calculate XP value for adventurers of this level (for an easy encounter)
base_xp_amount <- ( (a * (party_level^2)) + (b * party_level) + c )
# Define XP / player values from DMG
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))

# The formula for the line isn't perfect so we have another quick modification to make
if(party_level < 2) { easy_xp_amount <- base_xp_amount }
if(party_level >= 2 & party_level < 12) { easy_xp_amount <- (base_xp_amount + 55) }
if(party_level >= 12) { easy_xp_amount <- base_xp_amount }
# Grab the appropriate one
easy_xp_amount <- xp_df[xp_df$pc_level == party_level, ]$easy_xp

# If party level is *not* an integer, calculate by hand
} else {

# Identify the quadratic coefficients
## (Did this manually and got a decent fit for the line)
a <- 8.216374
b <- -26.49122
c <- 43.27485

# Calculate XP value for adventurers of this level (for an easy encounter)
base_xp_amount <- ( (a * (party_level^2)) + (b * party_level) + c )

# The formula for the line isn't perfect so we have another quick modification to make
if(party_level < 2) { easy_xp_amount <- base_xp_amount }
if(party_level >= 2 & party_level < 12) { easy_xp_amount <- (base_xp_amount + 55) }
if(party_level >= 12) { easy_xp_amount <- base_xp_amount }

} # Close player level integer vs. not conditional

# Now multiply as needed for the desired difficulty (this is straight from the DMG's table)
if(difficulty == "easy"){ single_xp_amount <- easy_xp_amount }
Expand Down
2 changes: 1 addition & 1 deletion man/xp_pool.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vignettes/dndr_99_dmg-vs-dndr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ ggplot(cr_actual, aes(x = cr, y = xp, shape = source)) +

### `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. Rather than embedding the DMG's table for encounter XP, `xp_pool` actually uses the formula for the line defining the XP-party level curve implicit in the DMG. This has the added benefit of being able to handle non-integer values for average party_level.
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`.

Expand Down

0 comments on commit 082d3d6

Please sign in to comment.