| Title: | Explore and Optimize Armor Combinations |
|---|---|
| Description: | This package contains exact stats for every armor piece in Dark Souls, at any upgrade level. It also has functions which produce a table of scored armor combinations, and the best value of a stat at each armor weight. This functionality is available in an interactive Shiny application. |
| Authors: | Package Maintainer [aut, cre] |
| Maintainer: | Package Maintainer <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 2.0.2 |
| Built: | 2026-11-06 00:32:33 UTC |
| Source: | https://github.com/ricewhitlam/darksoulsarmor |
This package contains exact stats for every armor piece in Dark Souls, at any upgrade level. It also has functions which produce a table of scored armor combinations, and the best value of a stat at each armor weight. This functionality is available in an interactive Shiny application.
Maintainer: Package Maintainer [email protected]
get.optimal.armor.combos, get.armor.tradeoffs,
get.tradeoff.efficiency, get.all.armor.combos,
armor.application
Character vector of areas in Dark Souls
This is not an official or complete listing of areas in the game.
Rather, it is the underlying list which is used to indicate
completed areas to get.optimal.armor.combos.
Runs a Shiny app for Dark Souls armor optimization. The sidebar sets the constraints and goals,
the same settings as get.optimal.armor.combos. 'Refresh Armor Data' fills two tabs:
Results, a table of the top-scoring combinations, and Trade-offs, a chart of the most of a chosen
stat any combination can reach at every armor weight (see get.armor.tradeoffs).
'Download Armor Data' saves both, with their settings, as an Excel workbook.
Some links are included in this app. To ensure that they work, launch the app in a browser.
armor.application(...)armor.application(...)
... |
Arguments are passed to |
No value is returned - rather, a Shiny application is run.
## Not run: armor.application() ## End(Not run)## Not run: armor.application() ## End(Not run)
Table of all unupgraded chest armor pieces
Character vector of starting classes in Dark Souls
This is the underlying list which is used to indicate
starting class to get.optimal.armor.combos.
data.table of all possible Dark Souls armor combinationsProduces a table of every possible armor combination in Dark Souls. This table is large (>10M rows and 17 columns, nearly 2 GB). Only call this function if there is enough available RAM to support this. All relevant metrics are included. All metrics are aggregated as would be expected with perhaps one exception: Durability is aggregated by taking the minimum across all armor pieces. Stats are exact (what the game computes, in 32-bit floating point), not rounded to the one decimal place the game displays. For example, Black Sorcerer Hat's strike defense is 5.1499996, which the game shows as 5.1.
get.all.armor.combos( regular.level = c("+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10")[1], twinkling.level = c("+0", "+1", "+2", "+3", "+4", "+5")[1] )get.all.armor.combos( regular.level = c("+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10")[1], twinkling.level = c("+0", "+1", "+2", "+3", "+4", "+5")[1] )
regular.level |
A length 1 |
twinkling.level |
A length 1 |
A data.table with one row per combination of every piece at the selected upgrade levels;
no filters apply. HEAD, CHEST, HANDS, LEGS; each defense and
resistance, added up; ARMOR_POISE, the pieces' poise added up; DURABILITY, the
lowest of the four; and ARMOR_WEIGHT, the armor's weight, added up in 32-bit floating
point as the game does.
## Not run: all.armor.combos <- get.all.armor.combos() ## End(Not run)## Not run: all.armor.combos <- get.all.armor.combos() ## End(Not run)
For a range of armor-weight limits, finds the combination with the most of one stat - the score, Poise, or a single defense or resistance - that weighs no more than each limit, with every other setting (filters, upgrade levels, rings, minima, ...) as given. This traces the trade-off between armor weight and that stat: e.g. how much more Poise each extra unit of armor weight can buy, or the lightest armor that reaches a Poise breakpoint.
A limit applies to the armor's listed weight (its pieces' weights added up). Every combination must also allow the movement type, checked as the game checks it - with the weapons, rings and the Mask of the Father's equip load bonus, in 32-bit floating point - so near a movement type's line, a combination within the limit can still be ruled out. Among combinations tied on the stat, the best-scoring one is returned.
get.armor.tradeoffs( metric = "SCORE", weight.step = 0.1, min.armor.weight = 0, max.armor.weight = NULL, ... )get.armor.tradeoffs( metric = "SCORE", weight.step = 0.1, min.armor.weight = 0, max.armor.weight = NULL, ... )
metric |
A length 1 |
weight.step |
A length 1 positive |
min.armor.weight |
A length 1 non-negative |
max.armor.weight |
A length 1 non-negative |
... |
Any other arguments of |
A list holding (1) the arguments that defined the curve and (2) a data.table with
one row per armor-weight limit (ARMOR_WEIGHT_LIMIT): the best achievable value of the
metric (BEST_VALUE), and the combination achieving it - its SCORE,
SCORE_QUALITY, ARMOR_WEIGHT, TOTAL_POISE, and pieces. Limits at which no
combination satisfies the other settings have NA values. Values are exact, as in
get.optimal.armor.combos.
poise.curve <- get.armor.tradeoffs(metric = "POISE", endurance.level = 40, movement = "Mid", weapons = c(right.1 = "Great Club"), weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4))poise.curve <- get.armor.tradeoffs(metric = "POISE", endurance.level = 40, movement = "Mid", weapons = c(right.1 = "Great Club"), weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4))
data.table of optimized Dark Souls armor combinationsProduces a table of optimized armor combinations in Dark Souls.
All relevant metrics are included, along with two score columns: SCORE, an
unbounded standardized score (the one the table is sorted on - see vignette("scoring")),
and SCORE_QUALITY, that score's exact rarity among every possible combination at the
selected upgrade levels (regardless of the other filters) - e.g. "Top 1 in 40" when 1 in
every 40 such combinations scores at least as well, or "Bottom 1 in 40" when 1 in every
40 scores at most as well.
Combinations with equal scores are ordered lighter first, then more poise, then more durability.
Stats are exact (what the game computes, in 32-bit floating point), not rounded to the one
decimal place the game displays. For example, Black Sorcerer Hat's strike defense is 5.1499996,
which the game shows as 5.1.
The table can be tailored to satisfy various constraints.
get.optimal.armor.combos( max.table.size = 1000, starting.class = classes[1], areas.completed = areas, upgrade.types = c("Regular", "Twinkling", "None"), head.filter = head.data.unupgraded$ARMOR, chest.filter = chest.data.unupgraded$ARMOR, hands.filter = hands.data.unupgraded$ARMOR, legs.filter = legs.data.unupgraded$ARMOR, regular.level = c("+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10")[1], twinkling.level = c("+0", "+1", "+2", "+3", "+4", "+5")[1], movement = c("Light", "Mid", "Fat", "Poop")[1], weapons = c(left.1 = "None", right.1 = "None", left.2 = "None", right.2 = "None"), endurance.level = 10, havel.ring = FALSE, favor.ring = FALSE, wolf.ring = FALSE, minima = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), weights, stat.scale = "sd", weights.mode = "individual" )get.optimal.armor.combos( max.table.size = 1000, starting.class = classes[1], areas.completed = areas, upgrade.types = c("Regular", "Twinkling", "None"), head.filter = head.data.unupgraded$ARMOR, chest.filter = chest.data.unupgraded$ARMOR, hands.filter = hands.data.unupgraded$ARMOR, legs.filter = legs.data.unupgraded$ARMOR, regular.level = c("+0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", "+8", "+9", "+10")[1], twinkling.level = c("+0", "+1", "+2", "+3", "+4", "+5")[1], movement = c("Light", "Mid", "Fat", "Poop")[1], weapons = c(left.1 = "None", right.1 = "None", left.2 = "None", right.2 = "None"), endurance.level = 10, havel.ring = FALSE, favor.ring = FALSE, wolf.ring = FALSE, minima = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), weights, stat.scale = "sd", weights.mode = "individual" )
max.table.size |
A length 1 |
starting.class |
A length 1 |
areas.completed |
A |
upgrade.types |
A |
head.filter |
A |
chest.filter |
Same as |
hands.filter |
Same as |
legs.filter |
Same as |
regular.level |
A length 1 |
twinkling.level |
A length 1 |
movement |
A length 1 |
weapons |
A |
endurance.level |
A length 1 |
havel.ring |
A length 1 |
favor.ring |
A length 1 |
wolf.ring |
A length 1 |
minima |
A |
weights |
A |
stat.scale |
A length 1 |
weights.mode |
A length 1 |
A list holding (1) args, the arguments as validated and used: clamped and rounded
where noted above, duplicates removed, an empty piece filter as its slot's "No ..." piece, and
weights divided by their sum; and (2) data, a data.table with one row per
combination, best first:
SCORE: the score, with mean 0 and standard deviation 1 across every possible
combination (see vignette("scoring")).
SCORE_QUALITY: its exact rarity among every combination at the selected upgrade
levels, e.g. "Top 1 in 40".
HEAD, CHEST, HANDS, LEGS: the pieces ("No Head" etc.
for an empty slot).
PHYS_DEF ... CURSE_RES: the set's totals at the selected upgrade levels.
DURABILITY: the lowest durability among the four pieces.
ARMOR_POISE: the pieces' poise added up. TOTAL_POISE: the same plus the
Wolf Ring's 40 when it's equipped.
POISE_TIMER: the poise recovery timer, in seconds: 5, less 10% for each piece
with any poise (4.5 with one, about 3.28 with four).
ARMOR_WEIGHT: the armor's weight, added up in 32-bit floating point as the game
does.
TOTAL_WEIGHT: everything carried, weapons then armor, added up as the game does.
EQUIP_LOAD: the maximum equip load, with the rings and, when it's worn, the Mask
of the Father's bonus.
PCT_LOAD: TOTAL_WEIGHT divided by EQUIP_LOAD (0.25 is the Light
line, 0.5 Mid, 1 Fat).
optimal.armor.combos <- get.optimal.armor.combos(endurance.level = 40, weapons = c(left.1 = "Grass Crest Shield", right.1 = "Longsword"), favor.ring = TRUE, movement = "Light", weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4)) ## At least 30 poise, scored only on physical and magic defense (physical counting double) poise.combos <- get.optimal.armor.combos(endurance.level = 40, movement = "Mid", minima = c(POISE = 30), weights = c(PHYS_DEF = 2, MAG_DEF = 1)) ## Physical defenses, elemental defenses and resistances weighted equally, each against its best possible total grouped.combos <- get.optimal.armor.combos(endurance.level = 40, movement = "Mid", weights = c(PHYSICAL_DEFENSES = 1, ELEMENTAL_DEFENSES = 1, RESISTANCES = 1), weights.mode = "grouped", stat.scale = "max")optimal.armor.combos <- get.optimal.armor.combos(endurance.level = 40, weapons = c(left.1 = "Grass Crest Shield", right.1 = "Longsword"), favor.ring = TRUE, movement = "Light", weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4)) ## At least 30 poise, scored only on physical and magic defense (physical counting double) poise.combos <- get.optimal.armor.combos(endurance.level = 40, movement = "Mid", minima = c(POISE = 30), weights = c(PHYS_DEF = 2, MAG_DEF = 1)) ## Physical defenses, elemental defenses and resistances weighted equally, each against its best possible total grouped.combos <- get.optimal.armor.combos(endurance.level = 40, movement = "Mid", weights = c(PHYSICAL_DEFENSES = 1, ELEMENTAL_DEFENSES = 1, RESISTANCES = 1), weights.mode = "grouped", stat.scale = "max")
Describes a trade-off curve (from get.armor.tradeoffs) as a sequence of pieces, so
that each step of the curve belongs to exactly one of them:
jumps: single steps where a little more weight buys a lot;
lines: regions with a steady rate, compared with the curve's average rate (its total gain divided by its total weight) - above it, extra weight buys more than average;
flats: regions where extra weight buys nothing;
with a boundary point wherever two regions (lines or flats) meet.
Points with no set (NA) are left out. The range is the curve's highest value less its
lowest, and the width its last weight less its first. A step smaller than a millionth of the
range counts as no change: such steps are 32-bit rounding between near-identical sets.
The rules were tuned on curves every 0.1 weight, get.armor.tradeoffs' default; on a
coarser curve each step merges several, so more of them read as jumps.
Jumps. A step is a jump if it rises at least a share of the range, and is worth at least an amount of weight at the rate around it: its rise divided by the rate of the rest of the gain within 4.5 weight either side of it, leaving out any jumps already chosen. So a big step in a steep stretch, whose neighbours rise as fast, isn't a jump; the stretch is a steep line. Jumps are chosen biggest first, up to a maximum; equal steps are taken together or not at all (a group that would go past the maximum is left out, along with everything smaller).
Flats. A run of steps with no gain at least a share of the width long, longest first, up to a maximum, equal lengths taken together or not at all.
Lines. Each stretch between the jumps and flats is fitted with the best 1, 2, 3 or 4 least-squares lines, each at least 2.5 weight wide, adding a line only if it brings the error (root mean square) down to at most 45% of what it was. A line's rate is its gain from the set before it to its last set, divided by the weight between them. A line with no gain is a flat.
Boundary points. Between two lines, the break moves to the biggest step within 1.25 weight of where the fit put it, which becomes the last step of the earlier line; from a line into a flat, it's the last step before the flat; from a flat into a line, the first step after it. A single step between two flats is itself the boundary point between them.
Promoted jumps. While fewer jumps than the maximum are in use, a boundary point's step becomes a jump if it's among the curve's largest steps (as many as the maximum, equal ones together), rises at least half the share of the range a jump otherwise needs, and is worth as much weight; biggest first, the curve described again around each.
get.tradeoff.efficiency(curve, jumps = "some", flats = "some")get.tradeoff.efficiency(curve, jumps = "some", flats = "some")
curve |
A curve from |
jumps |
How many jumps to look for: |
flats |
How many flats to look for: |
A list holding (1) average.slope, the curve's total gain divided by its total
weight, and (2) a data.table with one row per piece, in weight order: its TYPE
("jump", "line", "flat" or "boundary"); the weights it runs between
(FROM, TO: from the set before it to its last set, so that jumps, lines and flats
join end to end, a jump or boundary point being one step) and the curve's values there
(START_VALUE, END_VALUE), with their difference (GAIN); for lines and flats,
the rate as a multiple of the average (RATIO_TO_AVERAGE, NA when the average is 0)
and whether it's above average (ABOVE_AVERAGE); and for boundary points, the rates before
and after (RATIO_BEFORE, RATIO_AFTER). A boundary point's step also belongs to a
line beside it, except for one between two flats.
score.curve <- get.armor.tradeoffs(weight.step = 0.1, endurance.level = 40, movement = "Fat", weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4)) efficiency <- get.tradeoff.efficiency(score.curve) fewer.jumps <- get.tradeoff.efficiency(score.curve, jumps = "few")score.curve <- get.armor.tradeoffs(weight.step = 0.1, endurance.level = 40, movement = "Fat", weights = c(16, 16, 16, 16, 8, 8, 8, 4, 4, 4)) efficiency <- get.tradeoff.efficiency(score.curve) fewer.jumps <- get.tradeoff.efficiency(score.curve, jumps = "few")
Table of all unupgraded hand armor pieces
Table of all unupgraded head armor pieces
Name of armor piece
One of "Regular" (upgrades with regular titanite), "Twinkling" (upgrades with twinkling titanite), or "None" (cannot be upgraded).
If relevant, the starting class associated with the armor piece. Otherwise, "N/A".
Together, encode which areas make the piece available. Each holds one or more "|"-separated clauses combined with OR. A clause in AREA_MATCH_TYPE is "ALWAYS" (always available; its AREA_LIST clause is empty), "ANY" (available once at least one of its ";"-separated AREA_LIST areas is completed), or "ALL" (available once every one of its areas is completed).
A link to the armor piece on darksouls.wikidot.com.
The value of the relevant metric at +0. Strike, slash and thrust defense are the game's exact 32-bit values (e.g. 5.1499996 where the game shows 5.1); the other stats are exact as listed.
The additive modifier to the base stamina regeneration rate of 45 per second.
The multiplicative modifier to base character loudness.
Table of all unupgraded leg armor pieces
Table of every weapon, shield, bow and catalyst that can fill a weapon slot
Name of the weapon.
Its weight, which infusions and upgrades don't change.
Weight is the only property the package uses: a weapon's other effects (e.g. the Gargoyle Tail Axe raising unarmored poison and bleed resistance) change the character, not its armor, so they are outside the armor stats the package computes.