mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 00:32:40 +00:00
D&D: import Open5e SRD magic items as a vendored reference registry
fetch/gen magicitems subcommands vendor data/open5e/magicitems.json (237 SRD items) and classify them into a generated registry. magic_items.go holds the MagicItem struct + Kind enum + an init-time overlay merge where a hand-authored entry wins on ID collision, mirroring the spell and bestiary imports. Not yet wired into zone loot or the shop — that integration is a deliberate follow-up.
This commit is contained in:
5
NOTICE
5
NOTICE
@@ -3,9 +3,8 @@ GogoBee — third-party content attribution
|
||||
|
||||
Open5e SRD data
|
||||
---------------
|
||||
Portions of GogoBee's spell data (and, as they land, bestiary and equipment
|
||||
data) are derived from the Open5e dataset, which republishes the Systems
|
||||
Reference Document (SRD) 5.1.
|
||||
Portions of GogoBee's spell, bestiary, and magic-item data are derived from
|
||||
the Open5e dataset, which republishes the Systems Reference Document (SRD) 5.1.
|
||||
|
||||
Source: https://open5e.com / https://api.open5e.com
|
||||
License: Creative Commons Attribution 4.0 International (CC-BY-4.0)
|
||||
|
||||
291
cmd/open5e-import/magicitems.go
Normal file
291
cmd/open5e-import/magicitems.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// open5eMagicItem is the subset of the Open5e v1 magicitems schema we consume.
|
||||
// The vendored JSON keeps the full payload; unknown fields are ignored here.
|
||||
type open5eMagicItem struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Desc string `json:"desc"`
|
||||
Rarity string `json:"rarity"`
|
||||
RequiresAttunement string `json:"requires_attunement"`
|
||||
}
|
||||
|
||||
type magicItemsPage struct {
|
||||
Next string `json:"next"`
|
||||
Results []open5eMagicItem `json:"results"`
|
||||
}
|
||||
|
||||
// fetchMagicItems pages through the SRD magic-item list and writes the full
|
||||
// result set to data/open5e/magicitems.json, pretty-printed for a reviewable
|
||||
// diff.
|
||||
func fetchMagicItems() error {
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
var all []open5eMagicItem
|
||||
url := magicItemsAPIBase
|
||||
for url != "" {
|
||||
fmt.Fprintln(os.Stderr, "GET", url)
|
||||
page, err := getMagicItemsPage(client, url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
all = append(all, page.Results...)
|
||||
url = page.Next
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "fetched %d magic items\n", len(all))
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(magicItemsJSON), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := json.MarshalIndent(all, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out = append(out, '\n')
|
||||
return os.WriteFile(magicItemsJSON, out, 0o644)
|
||||
}
|
||||
|
||||
func getMagicItemsPage(client *http.Client, url string) (*magicItemsPage, error) {
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%s: HTTP %d", url, resp.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var page magicItemsPage
|
||||
if err := json.Unmarshal(body, &page); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &page, nil
|
||||
}
|
||||
|
||||
// ── Classifier ───────────────────────────────────────────────────────────────
|
||||
|
||||
// genMagicItem is a fully classified magic item ready for code emission. Fields
|
||||
// mirror plugin.MagicItem; enum-typed fields hold the Go identifier as a string.
|
||||
type genMagicItem struct {
|
||||
ID string
|
||||
Name string
|
||||
Kind string // plugin.MagicItemKind identifier
|
||||
Rarity string // plugin.DnDRarity identifier
|
||||
Attunement bool
|
||||
Slot string // plugin.DnDSlot identifier; "" = not equippable / ambiguous
|
||||
Value int
|
||||
Desc string
|
||||
}
|
||||
|
||||
// rarityEnum maps an Open5e rarity string to the plugin's DnDRarity identifier.
|
||||
// "artifact" folds into Legendary (the plugin has no artifact tier); "varies"
|
||||
// and unknown/blank values fall back to Common — the conservative bucket.
|
||||
var rarityEnum = map[string]string{
|
||||
"common": "RarityCommon",
|
||||
"uncommon": "RarityUncommon",
|
||||
"rare": "RarityRare",
|
||||
"very rare": "RarityVeryRare",
|
||||
"legendary": "RarityLegendary",
|
||||
"artifact": "RarityLegendary",
|
||||
}
|
||||
|
||||
// rarityValue is the sell-value midpoint per rarity, aligned with the §8.1
|
||||
// rarity brackets the zone-loot tables already use. Keyed by the Open5e rarity
|
||||
// string so the classifier can read it directly.
|
||||
var rarityValue = map[string]int{
|
||||
"common": 50,
|
||||
"uncommon": 150,
|
||||
"rare": 750,
|
||||
"very rare": 2500,
|
||||
"legendary": 8000,
|
||||
"artifact": 8000,
|
||||
}
|
||||
|
||||
// kindByPrefix maps the leading word of Open5e's free-text Type field
|
||||
// ("Wondrous item", "Weapon (any sword)", "Armor (medium or heavy)") to a
|
||||
// plugin.MagicItemKind identifier.
|
||||
var kindByPrefix = map[string]string{
|
||||
"wondrous": "MagicItemWondrous",
|
||||
"weapon": "MagicItemWeapon",
|
||||
"armor": "MagicItemArmor",
|
||||
"ring": "MagicItemRing",
|
||||
"wand": "MagicItemWand",
|
||||
"rod": "MagicItemRod",
|
||||
"staff": "MagicItemStaff",
|
||||
"potion": "MagicItemPotion",
|
||||
"scroll": "MagicItemScroll",
|
||||
}
|
||||
|
||||
// genMagicItems reads the vendored magicitems.json, classifies every SRD magic
|
||||
// item, and writes the generated Go data file.
|
||||
func genMagicItems() error {
|
||||
raw, err := os.ReadFile(magicItemsJSON)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read %s (run `fetch magicitems` first?): %w", magicItemsJSON, err)
|
||||
}
|
||||
var src []open5eMagicItem
|
||||
if err := json.Unmarshal(raw, &src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
items := make([]genMagicItem, 0, len(src))
|
||||
var attunement, slotted int
|
||||
for _, m := range src {
|
||||
g := classifyMagicItem(m)
|
||||
if g.Attunement {
|
||||
attunement++
|
||||
}
|
||||
if g.Slot != "" {
|
||||
slotted++
|
||||
}
|
||||
items = append(items, g)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].ID < items[j].ID })
|
||||
fmt.Fprintf(os.Stderr, "classified %d magic items (%d need attunement, %d slotted)\n",
|
||||
len(items), attunement, slotted)
|
||||
|
||||
out := emitMagicItems(items)
|
||||
if err := os.WriteFile(magicItemsGenGo, out, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "wrote", magicItemsGenGo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func classifyMagicItem(m open5eMagicItem) genMagicItem {
|
||||
g := genMagicItem{
|
||||
ID: strings.ReplaceAll(m.Slug, "-", "_"),
|
||||
Name: m.Name,
|
||||
Attunement: strings.TrimSpace(m.RequiresAttunement) != "",
|
||||
Desc: firstSentence(strings.TrimSpace(m.Desc), 200),
|
||||
}
|
||||
|
||||
rarity := strings.ToLower(strings.TrimSpace(m.Rarity))
|
||||
if enum, ok := rarityEnum[rarity]; ok {
|
||||
g.Rarity = enum
|
||||
} else {
|
||||
g.Rarity = "RarityCommon"
|
||||
}
|
||||
if v, ok := rarityValue[rarity]; ok {
|
||||
g.Value = v
|
||||
} else {
|
||||
g.Value = rarityValue["common"]
|
||||
}
|
||||
|
||||
// Kind: first word of the Type field. Anything unrecognised (the SRD has a
|
||||
// handful of bare or oddly-prefixed entries) falls back to Wondrous.
|
||||
kindWord := ""
|
||||
if fields := strings.Fields(strings.ToLower(m.Type)); len(fields) > 0 {
|
||||
kindWord = fields[0]
|
||||
}
|
||||
if enum, ok := kindByPrefix[kindWord]; ok {
|
||||
g.Kind = enum
|
||||
} else {
|
||||
g.Kind = "MagicItemWondrous"
|
||||
}
|
||||
|
||||
g.Slot = inferSlot(g.Kind, m.Name)
|
||||
return g
|
||||
}
|
||||
|
||||
// inferSlot makes a best-effort guess at the equip slot. Weapons/staves go to
|
||||
// the main hand, wands/rods to the off hand, armor to the chest, rings to a
|
||||
// ring slot. Wondrous items are name-sniffed (cloak, boots, amulet, …) and
|
||||
// left blank when nothing matches. Potions and scrolls are consumables and
|
||||
// never carry a slot.
|
||||
func inferSlot(kind, name string) string {
|
||||
switch kind {
|
||||
case "MagicItemWeapon", "MagicItemStaff":
|
||||
return "DnDSlotMainHand"
|
||||
case "MagicItemWand", "MagicItemRod":
|
||||
return "DnDSlotOffHand"
|
||||
case "MagicItemArmor":
|
||||
return "DnDSlotChest"
|
||||
case "MagicItemRing":
|
||||
return "DnDSlotRing1"
|
||||
case "MagicItemPotion", "MagicItemScroll":
|
||||
return ""
|
||||
}
|
||||
// Wondrous item: sniff the name for a wearable noun.
|
||||
low := strings.ToLower(name)
|
||||
for _, m := range []struct {
|
||||
kw string
|
||||
slot string
|
||||
}{
|
||||
{"amulet", "DnDSlotAmulet"}, {"necklace", "DnDSlotAmulet"},
|
||||
{"medallion", "DnDSlotAmulet"}, {"periapt", "DnDSlotAmulet"},
|
||||
{"brooch", "DnDSlotAmulet"}, {"pendant", "DnDSlotAmulet"},
|
||||
{"ring", "DnDSlotRing1"},
|
||||
{"cloak", "DnDSlotChest"}, {"cape", "DnDSlotChest"},
|
||||
{"mantle", "DnDSlotChest"}, {"robe", "DnDSlotChest"},
|
||||
{"armor", "DnDSlotChest"}, {"vestments", "DnDSlotChest"},
|
||||
{"boots", "DnDSlotFeet"}, {"slippers", "DnDSlotFeet"},
|
||||
{"gauntlet", "DnDSlotHands"}, {"gloves", "DnDSlotHands"},
|
||||
{"bracers", "DnDSlotHands"},
|
||||
{"helm", "DnDSlotHead"}, {"hat", "DnDSlotHead"},
|
||||
{"crown", "DnDSlotHead"}, {"circlet", "DnDSlotHead"},
|
||||
{"goggles", "DnDSlotHead"}, {"eyes", "DnDSlotHead"},
|
||||
{"headband", "DnDSlotHead"},
|
||||
} {
|
||||
if strings.Contains(low, m.kw) {
|
||||
return m.slot
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ── Code emission ────────────────────────────────────────────────────────────
|
||||
|
||||
func emitMagicItems(items []genMagicItem) []byte {
|
||||
var b bytes.Buffer
|
||||
b.WriteString(`// Code generated by cmd/open5e-import. DO NOT EDIT.
|
||||
//
|
||||
// Source: Open5e SRD magic-item dump (data/open5e/magicitems.json), 5e SRD
|
||||
// content under CC-BY-4.0 — see NOTICE. Regenerate with:
|
||||
//
|
||||
// go run ./cmd/open5e-import fetch magicitems
|
||||
// go run ./cmd/open5e-import gen magicitems
|
||||
//
|
||||
// Kind/Rarity/Slot/Value come from a free-text classifier; the hand-authored
|
||||
// magicItemOverlay overlays this slice and wins on ID collision, so any
|
||||
// misclassification here is correctable there rather than in the dump.
|
||||
|
||||
package plugin
|
||||
|
||||
func buildSRDMagicItems() []MagicItem {
|
||||
return []MagicItem{
|
||||
`)
|
||||
for _, m := range items {
|
||||
b.WriteString("\t\t{")
|
||||
fmt.Fprintf(&b, "ID: %q, Name: %q, Kind: %s, Rarity: %s", m.ID, m.Name, m.Kind, m.Rarity)
|
||||
if m.Attunement {
|
||||
b.WriteString(", Attunement: true")
|
||||
}
|
||||
if m.Slot != "" {
|
||||
fmt.Fprintf(&b, ", Slot: %s", m.Slot)
|
||||
}
|
||||
fmt.Fprintf(&b, ", Value: %d", m.Value)
|
||||
if m.Desc != "" {
|
||||
fmt.Fprintf(&b, ", Desc: %q", m.Desc)
|
||||
}
|
||||
b.WriteString("},\n")
|
||||
}
|
||||
b.WriteString("\t}\n}\n")
|
||||
return b.Bytes()
|
||||
}
|
||||
@@ -9,9 +9,8 @@
|
||||
// open5e-import fetch bestiary # vendor data/open5e/monsters.json from the API
|
||||
// open5e-import gen bestiary # classify → internal/plugin/bestiary_srd_data.go
|
||||
// open5e-import gen tuned # tuning formula → internal/plugin/bestiary_tuned_data.go
|
||||
//
|
||||
// `equipment` subcommands are planned; the dispatch below is structured to
|
||||
// grow into them without reshuffling.
|
||||
// open5e-import fetch magicitems # vendor data/open5e/magicitems.json from the API
|
||||
// open5e-import gen magicitems # classify → internal/plugin/magic_items_srd_data.go
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -33,6 +32,10 @@ const (
|
||||
monstersJSON = "data/open5e/monsters.json"
|
||||
bestiaryGenGo = "internal/plugin/bestiary_srd_data.go"
|
||||
tunedGenGo = "internal/plugin/bestiary_tuned_data.go"
|
||||
|
||||
magicItemsAPIBase = "https://api.open5e.com/v1/magicitems/?document__slug=wotc-srd&limit=50"
|
||||
magicItemsJSON = "data/open5e/magicitems.json"
|
||||
magicItemsGenGo = "internal/plugin/magic_items_srd_data.go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -47,6 +50,8 @@ func main() {
|
||||
must(fetchSpells())
|
||||
case "bestiary":
|
||||
must(fetchMonsters())
|
||||
case "magicitems":
|
||||
must(fetchMagicItems())
|
||||
default:
|
||||
usage()
|
||||
}
|
||||
@@ -58,6 +63,8 @@ func main() {
|
||||
must(genBestiary())
|
||||
case "tuned":
|
||||
must(genTuned())
|
||||
case "magicitems":
|
||||
must(genMagicItems())
|
||||
default:
|
||||
usage()
|
||||
}
|
||||
@@ -67,7 +74,7 @@ func main() {
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "usage: open5e-import fetch (spells|bestiary) | gen (spells|bestiary|tuned)")
|
||||
fmt.Fprintln(os.Stderr, "usage: open5e-import fetch (spells|bestiary|magicitems) | gen (spells|bestiary|tuned|magicitems)")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
|
||||
1898
data/open5e/magicitems.json
Normal file
1898
data/open5e/magicitems.json
Normal file
File diff suppressed because one or more lines are too long
67
internal/plugin/magic_items.go
Normal file
67
internal/plugin/magic_items.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package plugin
|
||||
|
||||
// Magic items — Open5e SRD import.
|
||||
//
|
||||
// Per the open5e integration plan, magic items are the one genuinely net-new
|
||||
// surface: EquipmentDef is a flavor ladder, not a structured magic-item table,
|
||||
// so there is nothing to extend. This file is the single new, non-dnd_-prefixed
|
||||
// table the plan allows for them.
|
||||
//
|
||||
// Like the bestiary staging table, this is a vendored *reference registry* —
|
||||
// 237 SRD magic items with classified Kind/Rarity/Slot/attunement and a
|
||||
// rarity-bracket sell value. It is deliberately NOT wired into the zone loot
|
||||
// tables or Luigi's shop yet; that integration is a separate follow-up pass.
|
||||
// buildSRDMagicItems() is the generated dump (see magic_items_srd_data.go);
|
||||
// magicItemOverlay is the hand-authored refinement layer that wins on ID
|
||||
// collision, mirroring the spell- and bestiary-import pattern.
|
||||
|
||||
// MagicItemKind is the coarse Open5e item category — the first word of Open5e's
|
||||
// free-text "type" field, normalised.
|
||||
type MagicItemKind string
|
||||
|
||||
const (
|
||||
MagicItemWondrous MagicItemKind = "wondrous"
|
||||
MagicItemWeapon MagicItemKind = "weapon"
|
||||
MagicItemArmor MagicItemKind = "armor"
|
||||
MagicItemRing MagicItemKind = "ring"
|
||||
MagicItemWand MagicItemKind = "wand"
|
||||
MagicItemRod MagicItemKind = "rod"
|
||||
MagicItemStaff MagicItemKind = "staff"
|
||||
MagicItemPotion MagicItemKind = "potion"
|
||||
MagicItemScroll MagicItemKind = "scroll"
|
||||
)
|
||||
|
||||
// MagicItem is one classified SRD magic item.
|
||||
type MagicItem struct {
|
||||
ID string
|
||||
Name string
|
||||
Kind MagicItemKind
|
||||
Rarity DnDRarity
|
||||
Attunement bool
|
||||
// Slot is a best-effort equip slot. It is "" for consumables (potions,
|
||||
// scrolls) and for wondrous items whose name carries no wearable noun.
|
||||
Slot DnDSlot
|
||||
Value int // sell value, derived from the §8.1-aligned rarity bracket
|
||||
Desc string // first-sentence summary of the SRD description
|
||||
}
|
||||
|
||||
// magicItemOverlay — hand-authored magic items that win on ID collision with
|
||||
// the generated SRD dump. Empty for now: the classifier output is the starting
|
||||
// point, and corrections (or wholly new items) land here rather than being
|
||||
// edited into the generated file.
|
||||
var magicItemOverlay []MagicItem
|
||||
|
||||
// magicItemRegistry is the merged lookup table: the generated SRD dump with the
|
||||
// hand-authored overlay layered on top.
|
||||
var magicItemRegistry = buildMagicItemRegistry()
|
||||
|
||||
func buildMagicItemRegistry() map[string]MagicItem {
|
||||
reg := make(map[string]MagicItem)
|
||||
for _, mi := range buildSRDMagicItems() {
|
||||
reg[mi.ID] = mi
|
||||
}
|
||||
for _, mi := range magicItemOverlay {
|
||||
reg[mi.ID] = mi // hand-authored wins
|
||||
}
|
||||
return reg
|
||||
}
|
||||
255
internal/plugin/magic_items_srd_data.go
Normal file
255
internal/plugin/magic_items_srd_data.go
Normal file
@@ -0,0 +1,255 @@
|
||||
// Code generated by cmd/open5e-import. DO NOT EDIT.
|
||||
//
|
||||
// Source: Open5e SRD magic-item dump (data/open5e/magicitems.json), 5e SRD
|
||||
// content under CC-BY-4.0 — see NOTICE. Regenerate with:
|
||||
//
|
||||
// go run ./cmd/open5e-import fetch magicitems
|
||||
// go run ./cmd/open5e-import gen magicitems
|
||||
//
|
||||
// Kind/Rarity/Slot/Value come from a free-text classifier; the hand-authored
|
||||
// magicItemOverlay overlays this slice and wins on ID collision, so any
|
||||
// misclassification here is correctable there rather than in the dump.
|
||||
|
||||
package plugin
|
||||
|
||||
func buildSRDMagicItems() []MagicItem {
|
||||
return []MagicItem{
|
||||
{ID: "adamantine_armor", Name: "Adamantine Armor", Kind: MagicItemArmor, Rarity: RarityUncommon, Slot: DnDSlotChest, Value: 150, Desc: "This suit of armor is reinforced with adamantine, one of the hardest substances in existence."},
|
||||
{ID: "amulet_of_health", Name: "Amulet of Health", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotAmulet, Value: 750, Desc: "Your Constitution score is 19 while you wear this amulet."},
|
||||
{ID: "amulet_of_proof_against_detection_and_location", Name: "Amulet of Proof against Detection and Location", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotAmulet, Value: 150, Desc: "While wearing this amulet, you are hidden from divination magic."},
|
||||
{ID: "amulet_of_the_planes", Name: "Amulet of the Planes", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotAmulet, Value: 2500, Desc: "While wearing this amulet, you can use an action to name a location that you are familiar with on another plane of existence."},
|
||||
{ID: "animated_shield", Name: "Animated Shield", Kind: MagicItemArmor, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "While holding this shield, you can speak its command word as a bonus action to cause it to animate."},
|
||||
{ID: "apparatus_of_the_crab", Name: "Apparatus of the Crab", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This item first appears to be a Large sealed iron barrel weighing 500 pounds."},
|
||||
{ID: "armor_of_invulnerability", Name: "Armor of Invulnerability", Kind: MagicItemArmor, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotChest, Value: 8000, Desc: "You have resistance to nonmagical damage while you wear this armor."},
|
||||
{ID: "armor_of_resistance", Name: "Armor of Resistance", Kind: MagicItemArmor, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "You have resistance to one type of damage while you wear this armor."},
|
||||
{ID: "armor_of_vulnerability", Name: "Armor of Vulnerability", Kind: MagicItemArmor, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "While wearing this armor, you have resistance to one of the following damage types: bludgeoning, piercing, or slashing."},
|
||||
{ID: "arrow_catching_shield", Name: "Arrow-Catching Shield", Kind: MagicItemArmor, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "You gain a +2 bonus to AC against ranged attacks while you wield this shield."},
|
||||
{ID: "arrow_of_slaying", Name: "Arrow of Slaying", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Slot: DnDSlotMainHand, Value: 2500, Desc: "An _arrow of slaying_ is a magic weapon meant to slay a particular kind of creature."},
|
||||
{ID: "bag_of_beans", Name: "Bag of Beans", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "Inside this heavy cloth bag are 3d4 dry beans."},
|
||||
{ID: "bag_of_devouring", Name: "Bag of Devouring", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Slot: DnDSlotRing1, Value: 2500, Desc: "This bag superficially resembles a _bag of holding_ but is a feeding orifice for a gigantic extradimensional creature."},
|
||||
{ID: "bag_of_holding", Name: "Bag of Holding", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This bag has an interior space considerably larger than its outside dimensions, roughly 2 feet in diameter at the mouth and 4 feet deep."},
|
||||
{ID: "bag_of_tricks", Name: "Bag of Tricks", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This ordinary bag, made from gray, rust, or tan cloth, appears empty."},
|
||||
{ID: "bead_of_force", Name: "Bead of Force", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This small black sphere measures 3/4 of an inch in diameter and weighs an ounce."},
|
||||
{ID: "belt_of_dwarvenkind", Name: "Belt of Dwarvenkind", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Value: 750, Desc: "While wearing this belt, you gain the following benefits:"},
|
||||
{ID: "belt_of_giant_strength", Name: "Belt of Giant Strength", Kind: MagicItemWondrous, Rarity: RarityCommon, Attunement: true, Value: 50, Desc: "While wearing this belt, your Strength score changes to a score granted by the belt."},
|
||||
{ID: "berserker_axe", Name: "Berserker Axe", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "boots_of_elvenkind", Name: "Boots of Elvenkind", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotFeet, Value: 150, Desc: "While you wear these boots, your steps make no sound, regardless of the surface you are moving across."},
|
||||
{ID: "boots_of_levitation", Name: "Boots of Levitation", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotFeet, Value: 750, Desc: "While you wear these boots, you can use an action to cast the _levitate_ spell on yourself at will."},
|
||||
{ID: "boots_of_speed", Name: "Boots of Speed", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotFeet, Value: 750, Desc: "While you wear these boots, you can use a bonus action and click the boots' heels together."},
|
||||
{ID: "boots_of_striding_and_springing", Name: "Boots of Striding and Springing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotRing1, Value: 150, Desc: "While you wear these boots, your walking speed becomes 30 feet, unless your walking speed is higher, and your speed isn't reduced if you are encumbered or wearing heavy armor."},
|
||||
{ID: "boots_of_the_winterlands", Name: "Boots of the Winterlands", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotFeet, Value: 150, Desc: "These furred boots are snug and feel quite warm."},
|
||||
{ID: "bowl_of_commanding_water_elementals", Name: "Bowl of Commanding Water Elementals", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "While this bowl is filled with water, you can use an action to speak the bowl's command word and summon a water elemental, as if you had cast the _conjure elemental_ spell."},
|
||||
{ID: "bracers_of_archery", Name: "Bracers of Archery", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHands, Value: 150, Desc: "While wearing these bracers, you have proficiency with the longbow and shortbow, and you gain a +2 bonus to damage rolls on ranged attacks made with such weapons."},
|
||||
{ID: "bracers_of_defense", Name: "Bracers of Defense", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotHands, Value: 750, Desc: "While wearing these bracers, you gain a +2 bonus to AC if you are wearing no armor and using no shield."},
|
||||
{ID: "brazier_of_commanding_fire_elementals", Name: "Brazier of Commanding Fire Elementals", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "While a fire burns in this brass brazier, you can use an action to speak the brazier's command word and summon a fire elemental, as if you had cast the _conjure elemental_ spell."},
|
||||
{ID: "brooch_of_shielding", Name: "Brooch of Shielding", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotAmulet, Value: 150, Desc: "While wearing this brooch, you have resistance to force damage, and you have immunity to damage from the _magic missile_ spell."},
|
||||
{ID: "broom_of_flying", Name: "Broom of Flying", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This wooden broom, which weighs 3 pounds, functions like a mundane broom until you stand astride it and speak its command word."},
|
||||
{ID: "candle_of_invocation", Name: "Candle of Invocation", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Value: 2500, Desc: "This slender taper is dedicated to a deity and shares that deity's alignment."},
|
||||
{ID: "cape_of_the_mountebank", Name: "Cape of the Mountebank", Kind: MagicItemWondrous, Rarity: RarityRare, Slot: DnDSlotChest, Value: 750, Desc: "This cape smells faintly of brimstone."},
|
||||
{ID: "carpet_of_flying", Name: "Carpet of Flying", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "You can speak the carpet's command word as an action to make the carpet hover and fly."},
|
||||
{ID: "censer_of_controlling_air_elementals", Name: "Censer of Controlling Air Elementals", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "While incense is burning in this censer, you can use an action to speak the censer's command word and summon an air elemental, as if you had cast the _conjure elemental_ spell."},
|
||||
{ID: "chime_of_opening", Name: "Chime of Opening", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This hollow metal tube measures about 1 foot long and weighs 1 pound."},
|
||||
{ID: "circlet_of_blasting", Name: "Circlet of Blasting", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotHead, Value: 150, Desc: "While wearing this circlet, you can use an action to cast the _scorching ray_ spell with it."},
|
||||
{ID: "cloak_of_arachnida", Name: "Cloak of Arachnida", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "This fine garment is made of black silk interwoven with faint silvery threads."},
|
||||
{ID: "cloak_of_displacement", Name: "Cloak of Displacement", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "While you wear this cloak, it projects an illusion that makes you appear to be standing in a place near your actual location, causing any creature to have disadvantage on attack rolls against you."},
|
||||
{ID: "cloak_of_elvenkind", Name: "Cloak of Elvenkind", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotChest, Value: 150, Desc: "While you wear this cloak with its hood up, Wisdom (Perception) checks made to see you have disadvantage, and you have advantage on Dexterity (Stealth) checks made to hide, as the cloak's color shifts…"},
|
||||
{ID: "cloak_of_protection", Name: "Cloak of Protection", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotChest, Value: 150, Desc: "You gain a +1 bonus to AC and saving throws while you wear this cloak."},
|
||||
{ID: "cloak_of_the_bat", Name: "Cloak of the Bat", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "While wearing this cloak, you have advantage on Dexterity (Stealth) checks."},
|
||||
{ID: "cloak_of_the_manta_ray", Name: "Cloak of the Manta Ray", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotChest, Value: 150, Desc: "While wearing this cloak with its hood up, you can breathe underwater, and you have a swimming speed of 60 feet."},
|
||||
{ID: "crystal_ball", Name: "Crystal Ball", Kind: MagicItemWondrous, Rarity: RarityCommon, Attunement: true, Value: 50, Desc: "The typical _crystal ball_, a very rare item, is about 6 inches in diameter."},
|
||||
{ID: "cube_of_force", Name: "Cube of Force", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Value: 750, Desc: "This cube is about an inch across."},
|
||||
{ID: "cubic_gate", Name: "Cubic Gate", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This cube is 3 inches across and radiates palpable magical energy."},
|
||||
{ID: "dagger_of_venom", Name: "Dagger of Venom", Kind: MagicItemWeapon, Rarity: RarityRare, Slot: DnDSlotMainHand, Value: 750, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "dancing_sword", Name: "Dancing Sword", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You can use a bonus action to toss this magic sword into the air and speak the command word."},
|
||||
{ID: "decanter_of_endless_water", Name: "Decanter of Endless Water", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This stoppered flask sloshes when shaken, as if it contains water."},
|
||||
{ID: "deck_of_illusions", Name: "Deck of Illusions", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This box contains a set of parchment cards."},
|
||||
{ID: "deck_of_many_things", Name: "Deck of Many Things", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "Usually found in a box or pouch, this deck contains a number of cards made of ivory or vellum."},
|
||||
{ID: "defender", Name: "Defender", Kind: MagicItemWeapon, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotMainHand, Value: 8000, Desc: "You gain a +3 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "demon_armor", Name: "Demon Armor", Kind: MagicItemArmor, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "While wearing this armor, you gain a +1 bonus to AC, and you can understand and speak Abyssal."},
|
||||
{ID: "dimensional_shackles", Name: "Dimensional Shackles", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "You can use an action to place these shackles on an incapacitated creature."},
|
||||
{ID: "dragon_scale_mail", Name: "Dragon Scale Mail", Kind: MagicItemArmor, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "Dragon scale mail is made of the scales of one kind of dragon."},
|
||||
{ID: "dragon_slayer", Name: "Dragon Slayer", Kind: MagicItemWeapon, Rarity: RarityRare, Slot: DnDSlotMainHand, Value: 750, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "dust_of_disappearance", Name: "Dust of Disappearance", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "Found in a small packet, this powder resembles very fine sand."},
|
||||
{ID: "dust_of_dryness", Name: "Dust of Dryness", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This small packet contains 1d6 + 4 pinches of dust."},
|
||||
{ID: "dust_of_sneezing_and_choking", Name: "Dust of Sneezing and Choking", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "Found in a small container, this powder resembles very fine sand."},
|
||||
{ID: "dwarven_plate", Name: "Dwarven Plate", Kind: MagicItemArmor, Rarity: RarityVeryRare, Slot: DnDSlotChest, Value: 2500, Desc: "While wearing this armor, you gain a +2 bonus to AC."},
|
||||
{ID: "dwarven_thrower", Name: "Dwarven Thrower", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You gain a +3 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "efficient_quiver", Name: "Efficient Quiver", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "Each of the quiver's three compartments connects to an extradimensional space that allows the quiver to hold numerous items while never weighing more than 2 pounds."},
|
||||
{ID: "efreeti_bottle", Name: "Efreeti Bottle", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This painted brass bottle weighs 1 pound."},
|
||||
{ID: "elemental_gem", Name: "Elemental Gem", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This gem contains a mote of elemental energy."},
|
||||
{ID: "elven_chain", Name: "Elven Chain", Kind: MagicItemArmor, Rarity: RarityRare, Slot: DnDSlotChest, Value: 750, Desc: "You gain a +1 bonus to AC while you wear this armor."},
|
||||
{ID: "eversmoking_bottle", Name: "Eversmoking Bottle", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "Smoke leaks from the lead-stoppered mouth of this brass bottle, which weighs 1 pound."},
|
||||
{ID: "eyes_of_charming", Name: "Eyes of Charming", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHead, Value: 150, Desc: "These crystal lenses fit over the eyes."},
|
||||
{ID: "eyes_of_minute_seeing", Name: "Eyes of Minute Seeing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotHead, Value: 150, Desc: "These crystal lenses fit over the eyes."},
|
||||
{ID: "eyes_of_the_eagle", Name: "Eyes of the Eagle", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHead, Value: 150, Desc: "These crystal lenses fit over the eyes."},
|
||||
{ID: "feather_token", Name: "Feather Token", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This tiny object looks like a feather."},
|
||||
{ID: "figurine_of_wondrous_power", Name: "Figurine of Wondrous Power", Kind: MagicItemWondrous, Rarity: RarityCommon, Value: 50, Desc: "A _figurine of wondrous power_ is a statuette of a beast small enough to fit in a pocket."},
|
||||
{ID: "flame_tongue", Name: "Flame Tongue", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "You can use a bonus action to speak this magic sword's command word, causing flames to erupt from the blade."},
|
||||
{ID: "folding_boat", Name: "Folding Boat", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This object appears as a wooden box that measures 12 inches long, 6 inches wide, and 6 inches deep."},
|
||||
{ID: "frost_brand", Name: "Frost Brand", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "When you hit with an attack using this magic sword, the target takes an extra 1d6 cold damage."},
|
||||
{ID: "gauntlets_of_ogre_power", Name: "Gauntlets of Ogre Power", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHands, Value: 150, Desc: "Your Strength score is 19 while you wear these gauntlets."},
|
||||
{ID: "gem_of_brightness", Name: "Gem of Brightness", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This prism has 50 charges."},
|
||||
{ID: "gem_of_seeing", Name: "Gem of Seeing", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Value: 750, Desc: "This gem has 3 charges."},
|
||||
{ID: "giant_slayer", Name: "Giant Slayer", Kind: MagicItemWeapon, Rarity: RarityRare, Slot: DnDSlotMainHand, Value: 750, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "glamoured_studded_leather", Name: "Glamoured Studded Leather", Kind: MagicItemArmor, Rarity: RarityRare, Slot: DnDSlotChest, Value: 750, Desc: "While wearing this armor, you gain a +1 bonus to AC."},
|
||||
{ID: "gloves_of_missile_snaring", Name: "Gloves of Missile Snaring", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotRing1, Value: 150, Desc: "These gloves seem to almost meld into your hands when you don them."},
|
||||
{ID: "gloves_of_swimming_and_climbing", Name: "Gloves of Swimming and Climbing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHands, Value: 150, Desc: "While wearing these gloves, climbing and swimming don't cost you extra movement, and you gain a +5 bonus to Strength (Athletics) checks made to climb or swim."},
|
||||
{ID: "goggles_of_night", Name: "Goggles of Night", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotHead, Value: 150, Desc: "While wearing these dark lenses, you have darkvision out to a range of 60 feet."},
|
||||
{ID: "hammer_of_thunderbolts", Name: "Hammer of Thunderbolts", Kind: MagicItemWeapon, Rarity: RarityLegendary, Slot: DnDSlotMainHand, Value: 8000, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "handy_haversack", Name: "Handy Haversack", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This backpack has a central pouch and two side pouches, each of which is an extradimensional space."},
|
||||
{ID: "hat_of_disguise", Name: "Hat of Disguise", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHead, Value: 150, Desc: "While wearing this hat, you can use an action to cast the _disguise self_ spell from it at will."},
|
||||
{ID: "headband_of_intellect", Name: "Headband of Intellect", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHead, Value: 150, Desc: "Your Intelligence score is 19 while you wear this headband."},
|
||||
{ID: "helm_of_brilliance", Name: "Helm of Brilliance", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotHead, Value: 2500, Desc: "This dazzling helm is set with 1d10 diamonds, 2d10 rubies, 3d10 fire opals, and 4d10 opals."},
|
||||
{ID: "helm_of_comprehending_languages", Name: "Helm of Comprehending Languages", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotHead, Value: 150, Desc: "While wearing this helm, you can use an action to cast the _comprehend languages_ spell from it at will."},
|
||||
{ID: "helm_of_telepathy", Name: "Helm of Telepathy", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotHead, Value: 150, Desc: "While wearing this helm, you can use an action to cast the _detect thoughts_ spell (save DC 13) from it."},
|
||||
{ID: "helm_of_teleportation", Name: "Helm of Teleportation", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotHead, Value: 750, Desc: "This helm has 3 charges."},
|
||||
{ID: "holy_avenger", Name: "Holy Avenger", Kind: MagicItemWeapon, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotMainHand, Value: 8000, Desc: "You gain a +3 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "horn_of_blasting", Name: "Horn of Blasting", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "You can use an action to speak the horn's command word and then blow the horn, which emits a thunderous blast in a 30-foot cone that is audible 600 feet away."},
|
||||
{ID: "horn_of_valhalla", Name: "Horn of Valhalla", Kind: MagicItemWondrous, Rarity: RarityCommon, Value: 50, Desc: "You can use an action to blow this horn."},
|
||||
{ID: "horseshoes_of_a_zephyr", Name: "Horseshoes of a Zephyr", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "These iron horseshoes come in a set of four."},
|
||||
{ID: "horseshoes_of_speed", Name: "Horseshoes of Speed", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "These iron horseshoes come in a set of four."},
|
||||
{ID: "immovable_rod", Name: "Immovable Rod", Kind: MagicItemRod, Rarity: RarityUncommon, Slot: DnDSlotOffHand, Value: 150, Desc: "This flat iron rod has a button on one end."},
|
||||
{ID: "instant_fortress", Name: "Instant Fortress", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "You can use an action to place this 1-inch metal cube on the ground and speak its command word."},
|
||||
{ID: "ioun_stone", Name: "Ioun Stone", Kind: MagicItemWondrous, Rarity: RarityCommon, Attunement: true, Value: 50, Desc: "An _Ioun stone_ is named after Ioun, a god of knowledge and prophecy revered on some worlds."},
|
||||
{ID: "iron_bands_of_binding", Name: "Iron Bands of Binding", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This rusty iron sphere measures 3 inches in diameter and weighs 1 pound."},
|
||||
{ID: "iron_flask", Name: "Iron Flask", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This iron bottle has a brass stopper."},
|
||||
{ID: "javelin_of_lightning", Name: "Javelin of Lightning", Kind: MagicItemWeapon, Rarity: RarityUncommon, Slot: DnDSlotMainHand, Value: 150, Desc: "This javelin is a magic weapon."},
|
||||
{ID: "lantern_of_revealing", Name: "Lantern of Revealing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "While lit, this hooded lantern burns for 6 hours on 1 pint of oil, shedding bright light in a 30-foot radius and dim light for an additional 30 feet."},
|
||||
{ID: "luck_blade", Name: "Luck Blade", Kind: MagicItemWeapon, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotMainHand, Value: 8000, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "mace_of_disruption", Name: "Mace of Disruption", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "When you hit a fiend or an undead with this magic weapon, that creature takes an extra 2d6 radiant damage."},
|
||||
{ID: "mace_of_smiting", Name: "Mace of Smiting", Kind: MagicItemWeapon, Rarity: RarityRare, Slot: DnDSlotMainHand, Value: 750, Desc: "You gain a +1 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "mace_of_terror", Name: "Mace of Terror", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "This magic weapon has 3 charges."},
|
||||
{ID: "mantle_of_spell_resistance", Name: "Mantle of Spell Resistance", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "You have advantage on saving throws against spells while you wear this cloak."},
|
||||
{ID: "manual_of_bodily_health", Name: "Manual of Bodily Health", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book contains health and diet tips, and its words are charged with magic."},
|
||||
{ID: "manual_of_gainful_exercise", Name: "Manual of Gainful Exercise", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book describes fitness exercises, and its words are charged with magic."},
|
||||
{ID: "manual_of_golems", Name: "Manual of Golems", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This tome contains information and incantations necessary to make a particular type of golem."},
|
||||
{ID: "manual_of_quickness_of_action", Name: "Manual of Quickness of Action", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book contains coordination and balance exercises, and its words are charged with magic."},
|
||||
{ID: "marvelous_pigments", Name: "Marvelous Pigments", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "Typically found in 1d4 pots inside a fine wooden box with a brush (weighing 1 pound in total), these pigments allow you to create three-dimensional objects by painting them in two dimensions."},
|
||||
{ID: "medallion_of_thoughts", Name: "Medallion of Thoughts", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotAmulet, Value: 150, Desc: "The medallion has 3 charges."},
|
||||
{ID: "mirror_of_life_trapping", Name: "Mirror of Life Trapping", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "When this 4-foot-tall mirror is viewed indirectly, its surface shows faint images of creatures."},
|
||||
{ID: "mithral_armor", Name: "Mithral Armor", Kind: MagicItemArmor, Rarity: RarityUncommon, Slot: DnDSlotChest, Value: 150, Desc: "Mithral is a light, flexible metal."},
|
||||
{ID: "necklace_of_adaptation", Name: "Necklace of Adaptation", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotAmulet, Value: 150, Desc: "While wearing this necklace, you can breathe normally in any environment, and you have advantage on saving throws made against harmful gases and vapors (such as _cloudkill_ and _stinking cloud_ effect…"},
|
||||
{ID: "necklace_of_fireballs", Name: "Necklace of Fireballs", Kind: MagicItemWondrous, Rarity: RarityRare, Slot: DnDSlotAmulet, Value: 750, Desc: "This necklace has 1d6 + 3 beads hanging from it."},
|
||||
{ID: "necklace_of_prayer_beads", Name: "Necklace of Prayer Beads", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotAmulet, Value: 750, Desc: "This necklace has 1d4 + 2 magic beads made from aquamarine, black pearl, or topaz."},
|
||||
{ID: "nine_lives_stealer", Name: "Nine Lives Stealer", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You gain a +2 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "oathbow", Name: "Oathbow", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "When you nock an arrow on this bow, it whispers in Elvish, \"Swift defeat to my enemies.\" When you use this weapon to make a ranged attack, you can, as a command phrase, say, \"Swift death to you who ha…"},
|
||||
{ID: "oil_of_etherealness", Name: "Oil of Etherealness", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "Beads of this cloudy gray oil form on the outside of its container and quickly evaporate."},
|
||||
{ID: "oil_of_sharpness", Name: "Oil of Sharpness", Kind: MagicItemPotion, Rarity: RarityVeryRare, Value: 2500, Desc: "This clear, gelatinous oil sparkles with tiny, ultrathin silver shards."},
|
||||
{ID: "oil_of_slipperiness", Name: "Oil of Slipperiness", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "This sticky black unguent is thick and heavy in the container, but it flows quickly when poured."},
|
||||
{ID: "orb_of_dragonkind", Name: "Orb of Dragonkind", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Value: 8000, Desc: "Ages past, elves and humans waged a terrible war against evil dragons."},
|
||||
{ID: "pearl_of_power", Name: "Pearl of Power", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Value: 150, Desc: "While this pearl is on your person, you can use an action to speak its command word and regain one expended spell slot."},
|
||||
{ID: "periapt_of_health", Name: "Periapt of Health", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotAmulet, Value: 150, Desc: "You are immune to contracting any disease while you wear this pendant."},
|
||||
{ID: "periapt_of_proof_against_poison", Name: "Periapt of Proof against Poison", Kind: MagicItemWondrous, Rarity: RarityRare, Slot: DnDSlotAmulet, Value: 750, Desc: "This delicate silver chain has a brilliant-cut black gem pendant."},
|
||||
{ID: "periapt_of_wound_closure", Name: "Periapt of Wound Closure", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotAmulet, Value: 150, Desc: "While you wear this pendant, you stabilize whenever you are dying at the start of your turn."},
|
||||
{ID: "philter_of_love", Name: "Philter of Love", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "The next time you see a creature within 10 minutes after drinking this philter, you become charmed by that creature for 1 hour."},
|
||||
{ID: "pipes_of_haunting", Name: "Pipes of Haunting", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "You must be proficient with wind instruments to use these pipes."},
|
||||
{ID: "pipes_of_the_sewers", Name: "Pipes of the Sewers", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Value: 150, Desc: "You must be proficient with wind instruments to use these pipes."},
|
||||
{ID: "plate_armor_of_etherealness", Name: "Plate Armor of Etherealness", Kind: MagicItemArmor, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotChest, Value: 8000, Desc: "While you're wearing this armor, you can speak its command word as an action to gain the effect of the _etherealness_ spell, which last for 10 minutes or until you remove the armor or use an action to…"},
|
||||
{ID: "portable_hole", Name: "Portable Hole", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This fine black cloth, soft as silk, is folded up to the dimensions of a handkerchief."},
|
||||
{ID: "potion_of_animal_friendship", Name: "Potion of Animal Friendship", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "When you drink this potion, you can cast the _animal friendship_ spell (save DC 13) for 1 hour at will."},
|
||||
{ID: "potion_of_clairvoyance", Name: "Potion of Clairvoyance", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "When you drink this potion, you gain the effect of the _clairvoyance_ spell."},
|
||||
{ID: "potion_of_climbing", Name: "Potion of Climbing", Kind: MagicItemPotion, Rarity: RarityCommon, Value: 50, Desc: "When you drink this potion, you gain a climbing speed equal to your walking speed for 1 hour."},
|
||||
{ID: "potion_of_diminution", Name: "Potion of Diminution", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "When you drink this potion, you gain the \"reduce\" effect of the _enlarge/reduce_ spell for 1d4 hours (no concentration required)."},
|
||||
{ID: "potion_of_flying", Name: "Potion of Flying", Kind: MagicItemPotion, Rarity: RarityVeryRare, Value: 2500, Desc: "When you drink this potion, you gain a flying speed equal to your walking speed for 1 hour and can hover."},
|
||||
{ID: "potion_of_gaseous_form", Name: "Potion of Gaseous Form", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "When you drink this potion, you gain the effect of the _gaseous form_ spell for 1 hour (no concentration required) or until you end the effect as a bonus action."},
|
||||
{ID: "potion_of_giant_strength", Name: "Potion of Giant Strength", Kind: MagicItemPotion, Rarity: RarityCommon, Value: 50, Desc: "When you drink this potion, your Strength score changes for 1 hour."},
|
||||
{ID: "potion_of_growth", Name: "Potion of Growth", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "When you drink this potion, you gain the \"enlarge\" effect of the _enlarge/reduce_ spell for 1d4 hours (no concentration required)."},
|
||||
{ID: "potion_of_healing", Name: "Potion of Healing", Kind: MagicItemPotion, Rarity: RarityCommon, Value: 50, Desc: "You regain hit points when you drink this potion."},
|
||||
{ID: "potion_of_heroism", Name: "Potion of Heroism", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "For 1 hour after drinking it, you gain 10 temporary hit points that last for 1 hour."},
|
||||
{ID: "potion_of_invisibility", Name: "Potion of Invisibility", Kind: MagicItemPotion, Rarity: RarityVeryRare, Value: 2500, Desc: "This potion's container looks empty but feels as though it holds liquid."},
|
||||
{ID: "potion_of_mind_reading", Name: "Potion of Mind Reading", Kind: MagicItemPotion, Rarity: RarityRare, Value: 750, Desc: "When you drink this potion, you gain the effect of the _detect thoughts_ spell (save DC 13)."},
|
||||
{ID: "potion_of_poison", Name: "Potion of Poison", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "This concoction looks, smells, and tastes like a _potion of healing_ or other beneficial potion."},
|
||||
{ID: "potion_of_resistance", Name: "Potion of Resistance", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "When you drink this potion, you gain resistance to one type of damage for 1 hour."},
|
||||
{ID: "potion_of_speed", Name: "Potion of Speed", Kind: MagicItemPotion, Rarity: RarityVeryRare, Value: 2500, Desc: "When you drink this potion, you gain the effect of the _haste_ spell for 1 minute (no concentration required)."},
|
||||
{ID: "potion_of_water_breathing", Name: "Potion of Water Breathing", Kind: MagicItemPotion, Rarity: RarityUncommon, Value: 150, Desc: "You can breathe underwater for 1 hour after drinking this potion."},
|
||||
{ID: "restorative_ointment", Name: "Restorative Ointment", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This glass jar, 3 inches in diameter, contains 1d4 + 1 doses of a thick mixture that smells faintly of aloe."},
|
||||
{ID: "ring_of_animal_influence", Name: "Ring of Animal Influence", Kind: MagicItemRing, Rarity: RarityRare, Slot: DnDSlotRing1, Value: 750, Desc: "This ring has 3 charges, and it regains 1d3 expended charges daily at dawn."},
|
||||
{ID: "ring_of_djinni_summoning", Name: "Ring of Djinni Summoning", Kind: MagicItemRing, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotRing1, Value: 8000, Desc: "While wearing this ring, you can speak its command word as an action to summon a particular djinni from the Elemental Plane of Air."},
|
||||
{ID: "ring_of_elemental_command", Name: "Ring of Elemental Command", Kind: MagicItemRing, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotRing1, Value: 8000, Desc: "This ring is linked to one of the four Elemental Planes."},
|
||||
{ID: "ring_of_evasion", Name: "Ring of Evasion", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "This ring has 3 charges, and it regains 1d3 expended charges daily at dawn."},
|
||||
{ID: "ring_of_feather_falling", Name: "Ring of Feather Falling", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "When you fall while wearing this ring, you descend 60 feet per round and take no damage from falling."},
|
||||
{ID: "ring_of_free_action", Name: "Ring of Free Action", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "While you wear this ring, difficult terrain doesn't cost you extra movement."},
|
||||
{ID: "ring_of_invisibility", Name: "Ring of Invisibility", Kind: MagicItemRing, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotRing1, Value: 8000, Desc: "While wearing this ring, you can turn invisible as an action."},
|
||||
{ID: "ring_of_jumping", Name: "Ring of Jumping", Kind: MagicItemRing, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotRing1, Value: 150, Desc: "While wearing this ring, you can cast the _jump_ spell from it as a bonus action at will, but can target only yourself when you do so."},
|
||||
{ID: "ring_of_mind_shielding", Name: "Ring of Mind Shielding", Kind: MagicItemRing, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotRing1, Value: 150, Desc: "While wearing this ring, you are immune to magic that allows other creatures to read your thoughts, determine whether you are lying, know your alignment, or know your creature type."},
|
||||
{ID: "ring_of_protection", Name: "Ring of Protection", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "You gain a +1 bonus to AC and saving throws while wearing this ring."},
|
||||
{ID: "ring_of_regeneration", Name: "Ring of Regeneration", Kind: MagicItemRing, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotRing1, Value: 2500, Desc: "While wearing this ring, you regain 1d6 hit points every 10 minutes, provided that you have at least 1 hit point."},
|
||||
{ID: "ring_of_resistance", Name: "Ring of Resistance", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "You have resistance to one damage type while wearing this ring."},
|
||||
{ID: "ring_of_shooting_stars", Name: "Ring of Shooting Stars", Kind: MagicItemRing, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotRing1, Value: 2500, Desc: "While wearing this ring in dim light or darkness, you can cast _dancing lights_ and _light_ from the ring at will."},
|
||||
{ID: "ring_of_spell_storing", Name: "Ring of Spell Storing", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "This ring stores spells cast into it, holding them until the attuned wearer uses them."},
|
||||
{ID: "ring_of_spell_turning", Name: "Ring of Spell Turning", Kind: MagicItemRing, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotRing1, Value: 8000, Desc: "While wearing this ring, you have advantage on saving throws against any spell that targets only you (not in an area of effect)."},
|
||||
{ID: "ring_of_swimming", Name: "Ring of Swimming", Kind: MagicItemRing, Rarity: RarityUncommon, Slot: DnDSlotRing1, Value: 150, Desc: "You have a swimming speed of 40 feet while wearing this ring."},
|
||||
{ID: "ring_of_telekinesis", Name: "Ring of Telekinesis", Kind: MagicItemRing, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotRing1, Value: 2500, Desc: "While wearing this ring, you can cast the _telekinesis_ spell at will, but you can target only objects that aren't being worn or carried."},
|
||||
{ID: "ring_of_the_ram", Name: "Ring of the Ram", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "This ring has 3 charges, and it regains 1d3 expended charges daily at dawn."},
|
||||
{ID: "ring_of_three_wishes", Name: "Ring of Three Wishes", Kind: MagicItemRing, Rarity: RarityLegendary, Slot: DnDSlotRing1, Value: 8000, Desc: "While wearing this ring, you can use an action to expend 1 of its 3 charges to cast the _wish_ spell from it."},
|
||||
{ID: "ring_of_warmth", Name: "Ring of Warmth", Kind: MagicItemRing, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotRing1, Value: 150, Desc: "While wearing this ring, you have resistance to cold damage."},
|
||||
{ID: "ring_of_water_walking", Name: "Ring of Water Walking", Kind: MagicItemRing, Rarity: RarityUncommon, Slot: DnDSlotRing1, Value: 150, Desc: "While wearing this ring, you can stand on and move across any liquid surface as if it were solid ground."},
|
||||
{ID: "ring_of_x_ray_vision", Name: "Ring of X-ray Vision", Kind: MagicItemRing, Rarity: RarityRare, Attunement: true, Slot: DnDSlotRing1, Value: 750, Desc: "While wearing this ring, you can use an action to speak its command word."},
|
||||
{ID: "robe_of_eyes", Name: "Robe of Eyes", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "This robe is adorned with eyelike patterns."},
|
||||
{ID: "robe_of_scintillating_colors", Name: "Robe of Scintillating Colors", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "This robe has 3 charges, and it regains 1d3 expended charges daily at dawn."},
|
||||
{ID: "robe_of_stars", Name: "Robe of Stars", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "This black or dark blue robe is embroidered with small white or silver stars."},
|
||||
{ID: "robe_of_the_archmagi", Name: "Robe of the Archmagi", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotChest, Value: 8000, Desc: "This elegant garment is made from exquisite cloth of white, gray, or black and adorned with silvery runes."},
|
||||
{ID: "robe_of_useful_items", Name: "Robe of Useful Items", Kind: MagicItemWondrous, Rarity: RarityUncommon, Slot: DnDSlotChest, Value: 150, Desc: "This robe has cloth patches of various shapes and colors covering it."},
|
||||
{ID: "rod_of_absorption", Name: "Rod of Absorption", Kind: MagicItemRod, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotOffHand, Value: 2500, Desc: "While holding this rod, you can use your reaction to absorb a spell that is targeting only you and not with an area of effect."},
|
||||
{ID: "rod_of_alertness", Name: "Rod of Alertness", Kind: MagicItemRod, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotOffHand, Value: 2500, Desc: "This rod has a flanged head and the following properties."},
|
||||
{ID: "rod_of_lordly_might", Name: "Rod of Lordly Might", Kind: MagicItemRod, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotOffHand, Value: 8000, Desc: "This rod has a flanged head, and it functions as a magic mace that grants a +3 bonus to attack and damage rolls made with it."},
|
||||
{ID: "rod_of_rulership", Name: "Rod of Rulership", Kind: MagicItemRod, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "You can use an action to present the rod and command obedience from each creature of your choice that you can see within 120 feet of you."},
|
||||
{ID: "rod_of_security", Name: "Rod of Security", Kind: MagicItemRod, Rarity: RarityVeryRare, Slot: DnDSlotOffHand, Value: 2500, Desc: "While holding this rod, you can use an action to activate it."},
|
||||
{ID: "rope_of_climbing", Name: "Rope of Climbing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "This 60-foot length of silk rope weighs 3 pounds and can hold up to 3,000 pounds."},
|
||||
{ID: "rope_of_entanglement", Name: "Rope of Entanglement", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "This rope is 30 feet long and weighs 3 pounds."},
|
||||
{ID: "scarab_of_protection", Name: "Scarab of Protection", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Value: 8000, Desc: "If you hold this beetle-shaped medallion in your hand for 1 round, an inscription appears on its surface revealing its magical nature."},
|
||||
{ID: "scimitar_of_speed", Name: "Scimitar of Speed", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You gain a +2 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "shield_of_missile_attraction", Name: "Shield of Missile Attraction", Kind: MagicItemArmor, Rarity: RarityRare, Attunement: true, Slot: DnDSlotChest, Value: 750, Desc: "While holding this shield, you have resistance to damage from ranged weapon attacks."},
|
||||
{ID: "slippers_of_spider_climbing", Name: "Slippers of Spider Climbing", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotFeet, Value: 150, Desc: "While you wear these light shoes, you can move up, down, and across vertical surfaces and upside down along ceilings, while leaving your hands free."},
|
||||
{ID: "sovereign_glue", Name: "Sovereign Glue", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This viscous, milky-white substance can form a permanent adhesive bond between any two objects."},
|
||||
{ID: "spell_scroll", Name: "Spell Scroll", Kind: MagicItemScroll, Rarity: RarityCommon, Value: 50, Desc: "A _spell scroll_ bears the words of a single spell, written in a mystical cipher."},
|
||||
{ID: "spellguard_shield", Name: "Spellguard Shield", Kind: MagicItemArmor, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotChest, Value: 2500, Desc: "While holding this shield, you have advantage on saving throws against spells and other magical effects, and spell attacks have disadvantage against you."},
|
||||
{ID: "sphere_of_annihilation", Name: "Sphere of Annihilation", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This 2-foot-diameter black sphere is a hole in the multiverse, hovering in space and stabilized by a magical field surrounding it."},
|
||||
{ID: "staff_of_charming", Name: "Staff of Charming", Kind: MagicItemStaff, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "While holding this staff, you can use an action to expend 1 of its 10 charges to cast _charm person_, _command_, _or comprehend languages_ from it using your spell save DC."},
|
||||
{ID: "staff_of_fire", Name: "Staff of Fire", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You have resistance to fire damage while you hold this staff."},
|
||||
{ID: "staff_of_frost", Name: "Staff of Frost", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You have resistance to cold damage while you hold this staff."},
|
||||
{ID: "staff_of_healing", Name: "Staff of Healing", Kind: MagicItemStaff, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "This staff has 10 charges."},
|
||||
{ID: "staff_of_power", Name: "Staff of Power", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "This staff can be wielded as a magic quarterstaff that grants a +2 bonus to attack and damage rolls made with it."},
|
||||
{ID: "staff_of_striking", Name: "Staff of Striking", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "This staff can be wielded as a magic quarterstaff that grants a +3 bonus to attack and damage rolls made with it."},
|
||||
{ID: "staff_of_swarming_insects", Name: "Staff of Swarming Insects", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "This staff has 10 charges and regains 1d6 + 4 expended charges daily at dawn."},
|
||||
{ID: "staff_of_the_magi", Name: "Staff of the Magi", Kind: MagicItemStaff, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotMainHand, Value: 8000, Desc: "This staff can be wielded as a magic quarterstaff that grants a +2 bonus to attack and damage rolls made with it."},
|
||||
{ID: "staff_of_the_python", Name: "Staff of the Python", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "You can use an action to speak this staff's command word and throw the staff on the ground within 10 feet of you."},
|
||||
{ID: "staff_of_the_woodlands", Name: "Staff of the Woodlands", Kind: MagicItemStaff, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "This staff can be wielded as a magic quarterstaff that grants a +2 bonus to attack and damage rolls made with it."},
|
||||
{ID: "staff_of_thunder_and_lightning", Name: "Staff of Thunder and Lightning", Kind: MagicItemStaff, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "This staff can be wielded as a magic quarterstaff that grants a +2 bonus to attack and damage rolls made with it."},
|
||||
{ID: "staff_of_withering", Name: "Staff of Withering", Kind: MagicItemStaff, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "This staff has 3 charges and regains 1d3 expended charges daily at dawn."},
|
||||
{ID: "stone_of_controlling_earth_elementals", Name: "Stone of Controlling Earth Elementals", Kind: MagicItemWondrous, Rarity: RarityRare, Value: 750, Desc: "If the stone is touching the ground, you can use an action to speak its command word and summon an earth elemental, as if you had cast the _conjure elemental_ spell."},
|
||||
{ID: "stone_of_good_luck_luckstone", Name: "Stone of Good Luck (Luckstone)", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Value: 150, Desc: "While this polished agate is on your person, you gain a +1 bonus to ability checks and saving throws."},
|
||||
{ID: "sun_blade", Name: "Sun Blade", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "This item appears to be a longsword hilt."},
|
||||
{ID: "sword_of_life_stealing", Name: "Sword of Life Stealing", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "When you attack a creature with this magic weapon and roll a 20 on the attack roll, that target takes an extra 3d6 necrotic damage, provided that the target isn't a construct or an undead."},
|
||||
{ID: "sword_of_sharpness", Name: "Sword of Sharpness", Kind: MagicItemWeapon, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotMainHand, Value: 2500, Desc: "When you attack an object with this magic sword and hit, maximize your weapon damage dice against the target."},
|
||||
{ID: "sword_of_wounding", Name: "Sword of Wounding", Kind: MagicItemWeapon, Rarity: RarityRare, Attunement: true, Slot: DnDSlotMainHand, Value: 750, Desc: "Hit points lost to this weapon's damage can be regained only through a short or long rest, rather than by regeneration, magic, or any other means."},
|
||||
{ID: "talisman_of_pure_good", Name: "Talisman of Pure Good", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Value: 8000, Desc: "This talisman is a mighty symbol of goodness."},
|
||||
{ID: "talisman_of_the_sphere", Name: "Talisman of the Sphere", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Value: 8000, Desc: "When you make an Intelligence (Arcana) check to control a _sphere of annihilation_ while you are holding this talisman, you double your proficiency bonus on the check."},
|
||||
{ID: "talisman_of_ultimate_evil", Name: "Talisman of Ultimate Evil", Kind: MagicItemWondrous, Rarity: RarityLegendary, Attunement: true, Value: 8000, Desc: "This item symbolizes unrepentant evil."},
|
||||
{ID: "tome_of_clear_thought", Name: "Tome of Clear Thought", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book contains memory and logic exercises, and its words are charged with magic."},
|
||||
{ID: "tome_of_leadership_and_influence", Name: "Tome of Leadership and Influence", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book contains guidelines for influencing and charming others, and its words are charged with magic."},
|
||||
{ID: "tome_of_understanding", Name: "Tome of Understanding", Kind: MagicItemWondrous, Rarity: RarityVeryRare, Value: 2500, Desc: "This book contains intuition and insight exercises, and its words are charged with magic."},
|
||||
{ID: "trident_of_fish_command", Name: "Trident of Fish Command", Kind: MagicItemWeapon, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotMainHand, Value: 150, Desc: "This trident is a magic weapon."},
|
||||
{ID: "universal_solvent", Name: "Universal Solvent", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This tube holds milky liquid with a strong alcohol smell."},
|
||||
{ID: "vicious_weapon", Name: "Vicious Weapon", Kind: MagicItemWeapon, Rarity: RarityRare, Slot: DnDSlotMainHand, Value: 750, Desc: "When you roll a 20 on your attack roll with this magic weapon, your critical hit deals an extra 2d6 damage of the weapon's type."},
|
||||
{ID: "vorpal_sword", Name: "Vorpal Sword", Kind: MagicItemWeapon, Rarity: RarityLegendary, Attunement: true, Slot: DnDSlotMainHand, Value: 8000, Desc: "You gain a +3 bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "wand_of_binding", Name: "Wand of Binding", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges for the following properties."},
|
||||
{ID: "wand_of_enemy_detection", Name: "Wand of Enemy Detection", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_fear", Name: "Wand of Fear", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges for the following properties."},
|
||||
{ID: "wand_of_fireballs", Name: "Wand of Fireballs", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_lightning_bolts", Name: "Wand of Lightning Bolts", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_magic_detection", Name: "Wand of Magic Detection", Kind: MagicItemWand, Rarity: RarityUncommon, Slot: DnDSlotOffHand, Value: 150, Desc: "This wand has 3 charges."},
|
||||
{ID: "wand_of_magic_missiles", Name: "Wand of Magic Missiles", Kind: MagicItemWand, Rarity: RarityUncommon, Slot: DnDSlotOffHand, Value: 150, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_paralysis", Name: "Wand of Paralysis", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_polymorph", Name: "Wand of Polymorph", Kind: MagicItemWand, Rarity: RarityVeryRare, Attunement: true, Slot: DnDSlotOffHand, Value: 2500, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_secrets", Name: "Wand of Secrets", Kind: MagicItemWand, Rarity: RarityUncommon, Slot: DnDSlotOffHand, Value: 150, Desc: "The wand has 3 charges."},
|
||||
{ID: "wand_of_the_war_mage_1_2_or_3", Name: "Wand of the War Mage, +1, +2, or +3", Kind: MagicItemWand, Rarity: RarityCommon, Attunement: true, Slot: DnDSlotOffHand, Value: 50, Desc: "While holding this wand, you gain a bonus to spell attack rolls determined by the wand's rarity."},
|
||||
{ID: "wand_of_web", Name: "Wand of Web", Kind: MagicItemWand, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotOffHand, Value: 150, Desc: "This wand has 7 charges."},
|
||||
{ID: "wand_of_wonder", Name: "Wand of Wonder", Kind: MagicItemWand, Rarity: RarityRare, Attunement: true, Slot: DnDSlotOffHand, Value: 750, Desc: "This wand has 7 charges."},
|
||||
{ID: "weapon_1_2_or_3", Name: "Weapon, +1, +2, or +3", Kind: MagicItemWeapon, Rarity: RarityCommon, Slot: DnDSlotMainHand, Value: 50, Desc: "You have a bonus to attack and damage rolls made with this magic weapon."},
|
||||
{ID: "well_of_many_worlds", Name: "Well of Many Worlds", Kind: MagicItemWondrous, Rarity: RarityLegendary, Value: 8000, Desc: "This fine black cloth, soft as silk, is folded up to the dimensions of a handkerchief."},
|
||||
{ID: "wind_fan", Name: "Wind Fan", Kind: MagicItemWondrous, Rarity: RarityUncommon, Value: 150, Desc: "While holding this fan, you can use an action to cast the _gust of wind_ spell (save DC 13) from it."},
|
||||
{ID: "winged_boots", Name: "Winged Boots", Kind: MagicItemWondrous, Rarity: RarityUncommon, Attunement: true, Slot: DnDSlotFeet, Value: 150, Desc: "While you wear these boots, you have a flying speed equal to your walking speed."},
|
||||
{ID: "wings_of_flying", Name: "Wings of Flying", Kind: MagicItemWondrous, Rarity: RarityRare, Attunement: true, Value: 750, Desc: "While wearing this cloak, you can use an action to speak its command word."},
|
||||
}
|
||||
}
|
||||
72
internal/plugin/magic_items_test.go
Normal file
72
internal/plugin/magic_items_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package plugin
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestMagicItemRegistryPopulated guards the generated SRD dump against an empty
|
||||
// or structurally broken regeneration.
|
||||
func TestMagicItemRegistryPopulated(t *testing.T) {
|
||||
if len(magicItemRegistry) < 200 {
|
||||
t.Fatalf("magic item registry too small: got %d, want >= 200", len(magicItemRegistry))
|
||||
}
|
||||
for id, mi := range magicItemRegistry {
|
||||
if mi.ID != id {
|
||||
t.Errorf("registry key %q != item ID %q", id, mi.ID)
|
||||
}
|
||||
if mi.Name == "" {
|
||||
t.Errorf("%s: empty Name", id)
|
||||
}
|
||||
if mi.Kind == "" {
|
||||
t.Errorf("%s: empty Kind", id)
|
||||
}
|
||||
if mi.Rarity == "" {
|
||||
t.Errorf("%s: empty Rarity", id)
|
||||
}
|
||||
if mi.Value <= 0 {
|
||||
t.Errorf("%s: non-positive Value %d", id, mi.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMagicItemSpotCheck pins a few classifier outputs so a regeneration that
|
||||
// changes the heuristics gets caught.
|
||||
func TestMagicItemSpotCheck(t *testing.T) {
|
||||
armor, ok := magicItemRegistry["adamantine_armor"]
|
||||
if !ok {
|
||||
t.Fatal("adamantine_armor missing from registry")
|
||||
}
|
||||
if armor.Kind != MagicItemArmor || armor.Rarity != RarityUncommon ||
|
||||
armor.Slot != DnDSlotChest || armor.Attunement {
|
||||
t.Errorf("adamantine_armor classified wrong: %+v", armor)
|
||||
}
|
||||
|
||||
amulet, ok := magicItemRegistry["amulet_of_health"]
|
||||
if !ok {
|
||||
t.Fatal("amulet_of_health missing from registry")
|
||||
}
|
||||
if amulet.Kind != MagicItemWondrous || amulet.Rarity != RarityRare ||
|
||||
amulet.Slot != DnDSlotAmulet || !amulet.Attunement {
|
||||
t.Errorf("amulet_of_health classified wrong: %+v", amulet)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMagicItemOverlayWins verifies the hand-authored overlay layers on top of
|
||||
// the generated dump and wins on ID collision. It exercises the merge with a
|
||||
// throwaway item so the assertion holds even while magicItemOverlay is empty.
|
||||
func TestMagicItemOverlayWins(t *testing.T) {
|
||||
saved := magicItemOverlay
|
||||
defer func() {
|
||||
magicItemOverlay = saved
|
||||
magicItemRegistry = buildMagicItemRegistry()
|
||||
}()
|
||||
|
||||
magicItemOverlay = []MagicItem{
|
||||
{ID: "adamantine_armor", Name: "Hand-Tuned Plate", Kind: MagicItemArmor,
|
||||
Rarity: RarityLegendary, Value: 9999},
|
||||
}
|
||||
magicItemRegistry = buildMagicItemRegistry()
|
||||
|
||||
got := magicItemRegistry["adamantine_armor"]
|
||||
if got.Name != "Hand-Tuned Plate" || got.Rarity != RarityLegendary {
|
||||
t.Errorf("overlay did not win on ID collision: %+v", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user