playlist-data-engine 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/parser/PlaylistParser.d.ts.map +1 -1
- package/dist/core/types/Playlist.d.ts +8 -0
- package/dist/core/types/Playlist.d.ts.map +1 -1
- package/dist/gateway-DUk4nCao.cjs +1 -0
- package/dist/{gateway-PjLtANkX.js → gateway-DyR4M-uH.js} +273 -254
- package/dist/gateway.js +1 -1
- package/dist/gateway.mjs +1 -1
- package/dist/playlist-data-engine.js +6 -6
- package/dist/playlist-data-engine.mjs +53 -53
- package/dist/utils/ipfsUtils.d.ts +7 -0
- package/dist/utils/ipfsUtils.d.ts.map +1 -1
- package/dist/utils/playlistUtils.d.ts +6 -0
- package/dist/utils/playlistUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/gateway-CtmUPK4J.cjs +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./unifyBeatMap-DIsNlck4.cjs"),z=require("./arweaveGatewayManager-C9SIMDTd.cjs"),O=require("./gateway-CtmUPK4J.cjs"),ds={0:"",1:"I",2:"II",3:"III",4:"IV",5:"V",6:"VI",7:"VII",8:"VIII",9:"IX",10:"X"},st=10,hs=10,ps=1e3,ia=1.5;function tr(s){return typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=10}function ms(s){return Math.max(0,Math.min(10,Math.floor(s)))}function ar(s){return typeof s=="string"&&["normal","aggressive"].includes(s)}const Ze={Greataxe:{name:"Greataxe",type:"weapon",rarity:"common",weight:7,damage:{dice:"1d12",damageType:"slashing"},weaponProperties:["two-handed"],spawnWeight:1,tags:["martial","melee","two-handed"],description:"A massive two-handed axe favored by barbarians for its devastating damage potential. Requires great strength to wield effectively."},Longsword:{name:"Longsword",type:"weapon",rarity:"common",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["versatile"],spawnWeight:1,tags:["martial","melee","versatile"],description:"A versatile sword usable with one or two hands. The classic weapon of knights and warriors throughout history."},Shortsword:{name:"Shortsword",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"piercing",versatile:"1d8"},weaponProperties:["finesse","light","versatile"],spawnWeight:1,tags:["martial","melee","finesse","light"],description:"A light, agile sword effective in close quarters. Can be used with Dexterity instead of Strength when wielded alone."},Rapier:{name:"Rapier",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["finesse"],spawnWeight:1,tags:["martial","melee","finesse"],description:"A slender, thrusting sword designed for speed and precision. Favored by duelists and rogues for its finesse property."},Quarterstaff:{name:"Quarterstaff",type:"weapon",rarity:"common",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile","two-handed"],spawnWeight:1,tags:["simple","melee","versatile"],description:"A simple wooden staff, often the first weapon of monks and spellcasters. Effective when used with two hands."},Mace:{name:"Mace",type:"weapon",rarity:"common",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile"],spawnWeight:1,tags:["simple","melee","versatile"],description:"A simple blunt weapon effective against armored foes. Common among clerics and those favoring divine righteousness."},Handaxe:{name:"Handaxe",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"slashing"},weaponProperties:["light","thrown","range_20_60"],spawnWeight:1,tags:["simple","melee","thrown","light"],description:"A small axe versatile in melee or as a thrown weapon. Popular among dwarves and rangers."},Dagger:{name:"Dagger",type:"weapon",rarity:"common",weight:1,damage:{dice:"1d4",damageType:"piercing",versatile:"1d6"},weaponProperties:["finesse","light","thrown","range_20_60"],spawnWeight:1,tags:["simple","melee","finesse","light","thrown"],description:"A lightweight weapon useful for melee and ranged attacks. Essential for rogues and as a backup weapon for all adventurers."},Dart:{name:"Dart",type:"weapon",rarity:"common",weight:.25,damage:{dice:"1d4",damageType:"piercing"},weaponProperties:["finesse","thrown","range_20_60"],spawnWeight:1,tags:["simple","ranged","thrown","finesse"],description:"A small throwing weapon, often carried in bulk. Lightweight but deals minimal damage."},Javelin:{name:"Javelin",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["thrown","range_30_120"],spawnWeight:1,tags:["simple","melee","thrown"],description:"A light spear designed for throwing. Effective at both melee and range."},"Light Crossbow":{name:"Light Crossbow",type:"weapon",rarity:"uncommon",weight:5,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["ammunition","range_80_320","two-handed","loading"],spawnWeight:1,tags:["simple","ranged","two-handed","ammunition"],description:"A compact crossbow requiring minimal training to use. Devastating at range but requires a reload action between shots."},"Hand Crossbow":{name:"Hand Crossbow",type:"weapon",rarity:"uncommon",weight:3,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["ammunition","range_30_120","light","loading"],spawnWeight:1,tags:["martial","ranged","light","ammunition"],description:"A small crossbow usable in one hand. Popular among rogues and those who need a ranged sidearm."},Longbow:{name:"Longbow",type:"weapon",rarity:"uncommon",weight:2,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["ammunition","range_150_600","two-handed","heavy"],spawnWeight:1,tags:["martial","ranged","two-handed","ammunition","heavy"],description:"A tall bow with impressive range and power. Requires significant strength to draw and training to use effectively."},"Martial Melee Weapon":{name:"Martial Melee Weapon",type:"weapon",rarity:"common",weight:3,damage:{dice:"1d8",damageType:"slashing"},weaponProperties:["versatile"],spawnWeight:.5,tags:["martial","melee","versatile","generic"],description:"A generic martial melee weapon. Used as a fallback when no specific weapon is specified."},"No Armor":{name:"No Armor",type:"armor",rarity:"common",weight:0,acBonus:10,spawnWeight:1,tags:["armor","light","no_armor"],description:"No armor worn. Relies entirely on Dexterity for defense. Maximum mobility but minimal protection."},"Leather Armor":{name:"Leather Armor",type:"armor",rarity:"common",weight:10,acBonus:11,properties:[{type:"passive_modifier",target:"ac",value:"11 + DEX",description:"Base AC: 11 + DEX"}],spawnWeight:1,tags:["armor","light"],description:"Light armor made from cured animal hides. Offers basic protection without restricting movement. Adds full Dexterity bonus to AC."},"Scale Mail":{name:"Scale Mail",type:"armor",rarity:"common",weight:45,acBonus:14,properties:[{type:"passive_modifier",target:"ac",value:"14 + min(DEX, 2)",description:"Base AC: 14 + DEX (max 2)"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"}],spawnWeight:1,tags:["armor","medium"],description:"Armor consisting of many overlapping metal scales. Solid protection but imposes disadvantage on Stealth checks."},"Chain Mail":{name:"Chain Mail",type:"armor",rarity:"common",weight:55,acBonus:16,properties:[{type:"passive_modifier",target:"ac",value:"16",description:"Fixed AC: 16"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:13,description:"Requires STR 13"}],spawnWeight:1,tags:["armor","heavy"],description:"Interlocking metal rings provide excellent protection. Requires Strength 13 to wear and imposes disadvantage on Stealth."},"Plate Armor":{name:"Plate Armor",type:"armor",rarity:"rare",weight:65,acBonus:18,properties:[{type:"passive_modifier",target:"ac",value:"18",description:"Fixed AC: 18"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:15,description:"Requires STR 15"}],spawnWeight:1,tags:["armor","heavy","rare"],description:"The finest armor available, consisting of shaped and fitted metal plates. Provides maximum protection but is expensive, heavy, and requires Strength 15."},Shield:{name:"Shield",type:"armor",rarity:"common",weight:6,acBonus:2,properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus"}],spawnWeight:1,tags:["armor","shield"],description:"A portable defensive barrier carried in one hand. Grants +2 to Armor Class. Cannot be used with two-handed weapons."},Spellbook:{name:"Spellbook",type:"item",rarity:"uncommon",weight:3,spawnWeight:1,tags:["gear","arcane","spellbook"],description:"A blank tome used by wizards to record their spells. Essential for preparing and casting wizard spells."},"Holy Symbol":{name:"Holy Symbol",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","divine","focus"],description:"An emblem of a deity used by clerics and paladins as a divine focus for spellcasting."},"Arcane Focus":{name:"Arcane Focus",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","arcane","focus"],description:"An object such as a crystal, orb, or wand used to channel arcane magic. Replaces material components for most spells."},"Druidic Focus":{name:"Druidic Focus",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","druid","focus"],description:"A natural object such as a staff, wand, or totem used by druids to channel primal magic."},"Component Pouch":{name:"Component Pouch",type:"box",rarity:"common",weight:2,spawnWeight:1,tags:["gear","components","spellcasting"],boxContents:{drops:[],consumeOnOpen:!1},description:"A pouch containing small compartments for storing material components needed for spells. A practical alternative to an arcane focus. Assumes to contain all material components without specific gold costs."},Lute:{name:"Lute",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","instrument","bard"],description:"A stringed musical instrument used by bards to perform and channel magical inspiration."},"Thieves' Tools":{name:"Thieves' Tools",type:"item",rarity:"uncommon",weight:1,grantsSkills:[{skillId:"thieves_tools",level:"proficient"}],spawnWeight:1,tags:["gear","tools","dexterity"],description:"A small collection of tools including picks, files, and crowbar. Essential for picking locks and disarming traps."},"Healer's Kit":{name:"Healer's Kit",type:"box",rarity:"common",weight:3,spawnWeight:1,tags:["gear","healing","consumable"],boxContents:{drops:[{pool:[{weight:100,itemName:"Medical Supply",quantity:10}]}],consumeOnOpen:!1},description:"A collection of bandages, salves, and tools for treating wounds. Contains 10 medical supplies. Essential for stabilizing dying creatures and providing medical aid."},Bedroll:{name:"Bedroll",type:"item",rarity:"common",weight:7,spawnWeight:1,tags:["gear","camp","comfort"],description:"A padded blanket roll used for sleeping outdoors. Essential comfort for adventurers on the road."},Rope:{name:"Rope",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","utility","climbing"],description:"50 feet of hempen rope. Essential for climbing, securing prisoners, and countless other utility purposes."},Backpack:{name:"Backpack",type:"box",rarity:"common",weight:5,spawnWeight:1,tags:["gear","container","storage"],boxContents:{drops:[],consumeOnOpen:!1},description:"A leather backpack for carrying adventuring gear. Essential storage for any traveling adventurer."},Torch:{name:"Torch",type:"item",rarity:"common",weight:1,properties:[{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],spawnWeight:1,tags:["gear","light","consumable"],description:"A wooden rod topped with flammable material. Burns for about an hour, shedding light in a 20-foot radius."},Waterskin:{name:"Waterskin",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","container","water"],description:"A leather container for holding water. Essential for surviving in wilderness and dungeon environments."},"Ink & Quill":{name:"Ink & Quill",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","writing","consumable"],description:"Ink and a quill pen for writing. Useful for recording maps, copying spells, and documenting adventures."},"Ball Bearings":{name:"Ball Bearings",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","utility","rogue"],description:"A bag containing 1,000 tiny metal balls. Scattered on the ground, they create a treacherous surface that can cause creatures to fall prone."},String:{name:"String",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","utility"],description:"10 feet of sturdy string. Useful for tripwires, tying items together, or countless other creative purposes."},Bell:{name:"Bell",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","utility","alarm"],description:"A small brass bell that rings when disturbed. Perfect for creating warning systems or signaling."},Crowbar:{name:"Crowbar",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","tool","utility"],description:"A heavy iron bar used for prying open doors, chests, and other stuck objects. Grants advantage on Strength checks to force objects open."},Hammer:{name:"Hammer",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","tool","utility"],description:"A sturdy hammer designed for driving pitons into walls and other heavy work. Essential for dungeon exploration."},Piton:{name:"Piton",type:"item",rarity:"common",weight:.25,spawnWeight:1,tags:["gear","climbing","utility"],description:"A metal spike with a ring at one end. Driven into cracks to create climbing anchors or secure rope lines."},"Small Knife":{name:"Small Knife",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","tool","utility"],description:"A small utility knife useful for cutting rope, skinning game, and other everyday tasks. Not designed for combat."},"Iron Key":{name:"Iron Key",type:"item",rarity:"common",weight:0,spawnWeight:.5,tags:["key","utility","consumable"],description:"A simple iron key that can unlock certain containers. Consumed when used to open a locked box."},"Golden Key":{name:"Golden Key",type:"item",rarity:"uncommon",weight:0,spawnWeight:.2,tags:["key","utility","consumable","valuable"],description:"An ornate golden key for unlocking valuable chests. Consumed when used to open a locked box."},"Skeleton Key":{name:"Skeleton Key",type:"item",rarity:"rare",weight:0,spawnWeight:.1,tags:["key","utility","consumable","universal"],description:"A master key that can open many different locks. Consumed when used to open a locked box."},Lockpick:{name:"Lockpick",type:"item",rarity:"common",weight:0,spawnWeight:.8,tags:["gear","rogue","consumable","tool"],description:"A simple lockpick. Multiple may be needed for complex locks, as they often break during use."},"Gold Coin":{name:"Gold Coin",type:"item",rarity:"common",weight:0,spawnWeight:0,tags:["currency","gold","money"],description:"A standard gold coin used as currency throughout the realm. Can be consumed to open certain magical locks."},"Locked Chest":{name:"Locked Chest",type:"box",rarity:"uncommon",weight:10,spawnWeight:.5,tags:["loot","treasure","locked"],boxContents:{openRequirements:[{itemName:"Iron Key"}],drops:[{pool:[{weight:100,gold:50}]},{pool:[{weight:50,itemName:"Shortsword"},{weight:30,itemName:"Leather Armor"},{weight:20,itemName:"Medical Supply",quantity:3}]}]},description:"A sturdy locked chest. Requires an Iron Key to open."},"Gilded Strongbox":{name:"Gilded Strongbox",type:"box",rarity:"rare",weight:15,spawnWeight:.3,tags:["loot","treasure","gold-locked"],boxContents:{openRequirements:[{itemName:"Gold Coin",quantity:100}],drops:[{pool:[{weight:100,gold:250}]},{pool:[{weight:40,itemName:"Longsword"},{weight:30,itemName:"Chain Mail"},{weight:20,itemName:"Scale Mail"},{weight:10,itemName:"Medical Supply",quantity:5}]}]},description:"A gilded strongbox with a magical lock. Consumes 100 Gold Coins to unlock."},"Royal Treasury Box":{name:"Royal Treasury Box",type:"box",rarity:"very_rare",weight:20,spawnWeight:.1,tags:["loot","treasure","royal","locked"],boxContents:{openRequirements:[{itemName:"Golden Key"},{itemName:"Gold Coin",quantity:200}],drops:[{pool:[{weight:100,gold:1e3}]},{pool:[{weight:30,itemName:"Plate Armor"},{weight:25,itemName:"Chain Mail"},{weight:25,itemName:"Greataxe"},{weight:20,itemName:"Longsword"}]},{pool:[{weight:60,itemName:"Medical Supply",quantity:10},{weight:40,itemName:"Thieves' Tools"}]}]},description:"A royal treasury box sealed with powerful magic. Requires a Golden Key and 200 Gold Coins to open."},"Thieves' Cache":{name:"Thieves' Cache",type:"box",rarity:"uncommon",weight:5,spawnWeight:.4,tags:["loot","treasure","rogue","locked"],boxContents:{openRequirements:[{itemName:"Lockpick",quantity:3}],drops:[{pool:[{weight:100,gold:75}]},{pool:[{weight:50,itemName:"Thieves' Tools"},{weight:30,itemName:"Dagger"},{weight:20,itemName:"Disguise Kit"}]}]},description:"A hidden cache with a complex lock. Requires 3 lockpicks to crack (consumed in the process)."},Candle:{name:"Candle",type:"item",rarity:"common",weight:0,properties:[{type:"special_property",target:"light",value:"dim_light_5ft",description:"Sheds dim light 5ft, bright 2ft"}],spawnWeight:1,tags:["gear","light","consumable"],description:"A simple wax candle that burns for about an hour. Provides dim light in a 5-foot radius, bright light in a 2-foot radius."},"Hooded Lantern":{name:"Hooded Lantern",type:"item",rarity:"common",weight:2,properties:[{type:"special_property",target:"light",value:"bright_light_30ft",description:"Sheds bright light 30ft, dim 30ft when unhooded"}],spawnWeight:1,tags:["gear","light"],description:"A metal lantern with a hood that can be raised or lowered to control the light. Burns for 6 hours on a flask of oil. Sheds bright light in a 30-foot cone and dim light for an additional 30 feet when unhooded."},"Oil Flask":{name:"Oil Flask",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","light","consumable"],description:"A flask of oil suitable for fueling lanterns. Can also be used as a weapon when thrown and ignited, dealing fire damage. Burns for 6 hours in a lantern."},Rations:{name:"Rations",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","food","consumable"],description:"One day's worth of dried meat, hard bread, and other preserved food. Essential for surviving long journeys without access to fresh food."},Water:{name:"Water",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","drink","consumable"],description:"Clean drinking water essential for survival. Typically stored in a waterskin and consumed during rest periods."},"Medical Supply":{name:"Medical Supply",type:"item",rarity:"common",weight:.3,spawnWeight:1,tags:["gear","healing","consumable"],description:"A single-use bandage, salve, or medical tool for treating wounds. Used to stabilize dying creatures or provide basic first aid."},Tinderbox:{name:"Tinderbox",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","fire","utility"],description:"A small container with flint, steel, and tinder for starting fires. Essential for lighting torches, campfires, and lanterns."},"Mess Kit":{name:"Mess Kit",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","camp","dining"],description:"A tin box containing a cup, bowl, and simple eating utensils. Collapses flat for easy packing and storage."},Blanket:{name:"Blanket",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","camp","comfort"],description:"A warm woolen blanket for sleeping. Provides basic warmth and comfort during rest in cold environments."},Costume:{name:"Costume",type:"item",rarity:"common",weight:4,spawnWeight:1,tags:["gear","clothing","performance"],description:"A set of performer's clothing suitable for stage appearances and entertaining. Includes colorful fabrics and decorative elements."},"Traveler's Clothes":{name:"Traveler's Clothes",type:"item",rarity:"common",weight:4,spawnWeight:1,tags:["gear","clothing"],description:"Sturdy, practical clothing designed for long journeys. Includes boots, a cloak, and weather-appropriate garb for the road."},Vestments:{name:"Vestments",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","clothing","religious"],description:"Ceremonial robes worn by priests and other religious figures. Often adorned with symbols of faith and made of fine materials."},"Alms Box":{name:"Alms Box",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","religious","container"],description:"A small wooden box with a slot for collecting alms and donations. Used by priests and mendicants to gather charitable offerings."},Incense:{name:"Incense",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","religious","consumable"],description:"A block of aromatic incense used in religious ceremonies and rituals. Burns slowly to produce fragrant smoke during prayers and worship."},Censer:{name:"Censer",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","religious"],description:"A metal container suspended on chains, used for burning incense during religious ceremonies. Often made of silver or brass and adorned with religious symbols."},"Book of Lore":{name:"Book of Lore",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","book","knowledge"],description:"A scholarly tome containing accumulated knowledge on various subjects. Scholars and wizards often carry these to reference arcane theories, historical accounts, or arcane formulas."},"Bottle of Ink":{name:"Bottle of Ink",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","consumable"],description:"A small glass bottle containing black ink, sealed with a stopper. Used with an ink pen for writing on parchment or paper."},"Ink Pen":{name:"Ink Pen",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing"],description:"A wooden pen with a metal nib, designed to be dipped in ink for writing. Essential for scribes, scholars, and anyone needing to keep written records."},Parchment:{name:"Parchment",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","consumable"],description:"A single sheet of parchment made from animal skin, used for writing important documents, letters, or arcane inscriptions. More durable than paper but also more expensive."},"Bag of Sand":{name:"Bag of Sand",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","utility"],description:"A small cloth bag filled with fine sand. Sprinkled over freshly written text to absorb excess ink and speed drying, preventing smudges and blots."},"Disguise Kit":{name:"Disguise Kit",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","deception","rogue"],description:"A kit containing cosmetics, hair dye, and small props to create disguises. Includes makeup, false facial hair, and other materials needed to change one's appearance. Essential for spies, actors, and rogues who need to assume different identities."},"Burglar's Pack":{name:"Burglar's Pack",type:"box",rarity:"common",weight:44,spawnWeight:1,tags:["gear","pack","rogue"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Ball Bearings",quantity:1e3}]},{pool:[{weight:100,itemName:"String"}]},{pool:[{weight:100,itemName:"Bell"}]},{pool:[{weight:100,itemName:"Candle",quantity:5}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Hooded Lantern"}]},{pool:[{weight:100,itemName:"Oil Flask",quantity:2}]},{pool:[{weight:100,itemName:"Rations",quantity:5}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack containing gear for a rogue: a backpack, a bag of 1,000 ball bearings, 10 feet of string, a bell, 5 candles, a crowbar, a hammer, 10 pitons, a hooded lantern, 2 flasks of oil, 5 days' rations, a tinderbox, and a waterskin. Includes a pack with 50 feet of hempen rope strapped to the side."},"Explorer's Pack":{name:"Explorer's Pack",type:"box",rarity:"common",weight:59,spawnWeight:1,tags:["gear","pack","general"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Mess Kit"}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]}],consumeOnOpen:!0},description:"A backpack containing gear for wilderness exploration: a backpack, a bedroll, mess kit, tinderbox, 10 torches, and 10 days' rations. Includes a waterskin and 50 feet of hempen rope."},"Entertainer's Pack":{name:"Entertainer's Pack",type:"box",rarity:"common",weight:58,spawnWeight:1,tags:["gear","pack","bard"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Costume",quantity:2}]},{pool:[{weight:100,itemName:"Candle",quantity:5}]},{pool:[{weight:100,itemName:"Rations",quantity:5}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Disguise Kit"}]}],consumeOnOpen:!0},description:"A backpack containing a bard's performance gear: a backpack, a bedroll, 2 costumes, 5 candles, 5 days' rations, a waterskin, and a disguise kit."},"Priest's Pack":{name:"Priest's Pack",type:"box",rarity:"common",weight:33,spawnWeight:1,tags:["gear","pack","cleric"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Blanket"}]},{pool:[{weight:100,itemName:"Candle",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Alms Box"}]},{pool:[{weight:100,itemName:"Incense",quantity:2}]},{pool:[{weight:100,itemName:"Censer"}]},{pool:[{weight:100,itemName:"Vestments"}]},{pool:[{weight:100,itemName:"Rations",quantity:2}]},{pool:[{weight:100,itemName:"Waterskin"}]}],consumeOnOpen:!0},description:"A backpack containing a priest's supplies: a backpack, a blanket, 10 candles, a tinderbox, an alms box, 2 blocks of incense, a censer, vestments, 2 days' rations, and a waterskin."},"Dungeon Delver's Pack":{name:"Dungeon Delver's Pack",type:"box",rarity:"common",weight:48,spawnWeight:1,tags:["gear","pack","dungeon"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack optimized for dungeon exploration. Contains a backpack, crowbar, hammer, 10 pitons, 10 torches, a tinderbox, 10 days' rations, and a waterskin. Includes 50 feet of hempen rope."},"Dungeoneer's Pack":{name:"Dungeoneer's Pack",type:"box",rarity:"common",weight:48,spawnWeight:1,tags:["gear","pack","dungeon"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack optimized for underground exploration. Contains a backpack, crowbar, hammer, 10 pitons, 10 torches, a tinderbox, 10 days' rations, and a waterskin. Includes 50 feet of hempen rope."},"Scholar's Pack":{name:"Scholar's Pack",type:"box",rarity:"common",weight:49,spawnWeight:1,tags:["gear","pack","wizard"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Book of Lore"}]},{pool:[{weight:100,itemName:"Bottle of Ink"}]},{pool:[{weight:100,itemName:"Ink Pen"}]},{pool:[{weight:100,itemName:"Parchment",quantity:10}]},{pool:[{weight:100,itemName:"Bag of Sand"}]}],consumeOnOpen:!0},description:"A backpack containing a scholar's supplies: a backpack, a book of lore, a bottle of ink, an ink pen, 10 sheets of parchment, and a little bag of sand."},"Traveler's Pack":{name:"Traveler's Pack",type:"box",rarity:"common",weight:43,spawnWeight:1,tags:["gear","pack","general"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Mess Kit"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Traveler's Clothes"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack for long-distance travel: a backpack, a bedroll, a mess kit, 10 days' rations, a waterskin, traveler's clothes, and 50 feet of hempen rope."},Arrow:{name:"Arrow",type:"item",rarity:"common",weight:.05,spawnWeight:1,tags:["ammunition","bow"],description:"Ammunition for a bow. Typically carried in quivers of 20 arrows. Essential for archers."},Bolt:{name:"Bolt",type:"item",rarity:"common",weight:.075,spawnWeight:1,tags:["ammunition","crossbow"],description:"Ammunition for a crossbow. Shorter and heavier than arrows, designed for crossbow use."},Insignia:{name:"Insignia",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","roleplay","insignia"],description:"A badge, banner, or token representing rank, membership, or authority. Useful for roleplaying and establishing credentials."},"Martial Arts":{name:"Martial Arts",type:"weapon",rarity:"common",weight:0,damage:{dice:"1d4",damageType:"bludgeoning",versatile:"1d6"},weaponProperties:["finesse","unarmed"],spawnWeight:1,tags:["monk","unarmed","natural"],description:"The monk's unarmed strike. Uses Dexterity instead of Strength and deals increased damage at higher levels."}};class U{static rollDie(e){if(e<1)throw new Error("Die must have at least 1 side");return Math.floor(Math.random()*e)+1}static rollMultipleDice(e,t){const a=[];for(let i=0;i<e;i++)a.push(this.rollDie(t));return a}static parseDiceFormula(e){const t=e.match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!t)throw new Error(`Invalid dice formula: ${e}`);const a=parseInt(t[1],10),i=parseInt(t[2],10),n=t[3]?parseInt(t[3],10):0,r=this.rollMultipleDice(a,i),o=r.reduce((l,c)=>l+c,0)+n;return{diceCount:a,diceSides:i,modifier:n,rolls:r,total:o}}static rollD20(){return this.rollDie(20)}static rollWithAdvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.max(e,t)}}static rollWithDisadvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.min(e,t)}}static rollInitiative(e){return this.rollD20()+e}static isCriticalHit(e){return e===20}static isCriticalMiss(e){return e===1}static doubleDamage(e){return[...e,...e]}static calculateDamage(e,t,a=!1){let n=this.parseDiceFormula(e).rolls;a&&(n=this.doubleDamage(n));const r=n.reduce((o,l)=>o+l,0)+t;return{diceFormula:e,rolls:n,modifier:t,total:r,isCritical:a}}static rollSavingThrow(e,t=0){return this.rollD20()+e+t}static rollAbilityCheck(e,t=0){return this.rollD20()+e+t}static seededRoll(e){const t=Math.sin(e)*1e4;return Math.floor((t-Math.floor(t))*20)+1}static rollPercentile(){return this.rollDie(100)}}const ir={1:[0,2,0,0,0,0,0,0,0,0],2:[0,3,0,0,0,0,0,0,0,0],3:[0,4,2,0,0,0,0,0,0,0],4:[0,4,3,0,0,0,0,0,0,0],5:[0,4,3,2,0,0,0,0,0,0],6:[0,4,3,3,0,0,0,0,0,0],7:[0,4,3,3,1,0,0,0,0,0],8:[0,4,3,3,2,0,0,0,0,0],9:[0,4,3,3,3,1,0,0,0,0],10:[0,4,3,3,3,2,0,0,0,0],11:[0,4,3,3,3,2,1,0,0,0],12:[0,4,3,3,3,2,1,0,0,0],13:[0,4,3,3,3,2,1,1,0,0],14:[0,4,3,3,3,2,1,1,0,0],15:[0,4,3,3,3,2,1,1,1,0],16:[0,4,3,3,3,2,1,1,1,0],17:[0,4,3,3,3,2,1,1,1,1],18:[0,4,3,3,3,3,1,1,1,1],19:[0,4,3,3,3,3,2,1,1,1],20:[0,4,3,3,3,3,2,2,1,1]};function sa(s){const e=ir[s];if(!e)return{};const t={};for(let a=1;a<e.length;a++)e[a]>0&&(t[a]=e[a]);return t}const di={charm:{name:"Charmed",requiresConcentration:!0,mechanicalEffects:{disadvantageOnAttackNonSource:!0}},frighten:{name:"Frightened",requiresConcentration:!0,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},stun:{name:"Stunned",requiresConcentration:!1,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0,skipTurn:!0}},paralyze:{name:"Paralyzed",requiresConcentration:!1,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0,skipTurn:!0}},restrain:{name:"Restrained",requiresConcentration:!0,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0}},poison:{name:"Poisoned",requiresConcentration:!1,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},blind:{name:"Blinded",requiresConcentration:!1,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},deafen:{name:"Deafened",requiresConcentration:!1},burn:{name:"Burning",requiresConcentration:!1}};class V{constructor(e){this.diceRoller=e}castSpell(e,t,a){const i=t.level??0;if(!this.hasSpellSlot(e,i))return{success:!1,spellName:t.name,caster:e,targets:a,effectsApplied:[],spellSlotUsed:i,description:`${e.character.name} tried to cast ${t.name} but has no spell slots available!`};this.consumeSpellSlot(e,i);const n=[];let r,o;const l=t.damage_dice??t.damage,c=t.damage_type??t.damageType,u=t.saving_throw??t.save;if(l&&c){if(t.attack_roll)r=this.diceRoller?this.diceRoller.calculateDamage(l,0,!1):U.calculateDamage(l,0,!1);else if(u){o=this.calculateSaveDC(e,u);for(const m of a){const v=u.toLowerCase()==="dexterity"&&m.statusEffects.some(E=>E.mechanicalEffects?.disadvantageOnDexSaves);this.makeSavingThrow(m,u,o,v)||(r=this.diceRoller?this.diceRoller.calculateDamage(l,0,!1):U.calculateDamage(l,0,!1),m.currentHP-=r.total,m.currentHP<0&&(m.currentHP=0,m.isDefeated=!0))}}}const d=t.tags??[],h=new Set;for(const m of d){const f=di[m];if(f){h.add(f.name);const v={name:f.name,description:`${f.name} by ${e.character.name}`,duration:1,source:e.id,hasConcentration:f.requiresConcentration,mechanicalEffects:f.mechanicalEffects};for(const w of a)n.push({...v})}}const p=[t.description,t.effect].filter(Boolean).join(" ").toLowerCase();if(!h.has("Charmed")&&p.includes("charm")){const m={name:"Charmed",description:`Charmed by ${e.character.name}`,duration:1,source:e.id,hasConcentration:!0};for(const f of a)n.push({...m})}if(!h.has("Frightened")&&p.includes("frighten")){const m={name:"Frightened",description:`Frightened of ${e.character.name}`,duration:1,source:e.id,hasConcentration:!0};for(const f of a)n.push({...m})}const g=`${e.character.name} casts ${t.name} (Level ${i}) at ${a.map(m=>m.character.name).join(", ")}`;return{success:!0,spellName:t.name,caster:e,targets:a,saveDC:o,damage:r,effectsApplied:n,spellSlotUsed:i,description:g}}hasSpellSlot(e,t){return t===0?!0:!e.spellSlots||!e.spellSlots[t]?!1:e.spellSlots[t]>0}consumeSpellSlot(e,t){t!==0&&(e.spellSlots||(e.spellSlots={}),e.spellSlots[t]||(e.spellSlots[t]=0),e.spellSlots[t]--)}restoreSpellSlots(e){e.spellSlots=sa(e.character.level)}calculateSaveDC(e,t){const a=t.toLowerCase(),i=e.character.ability_modifiers[a]||0,n=e.character.proficiency_bonus;return 8+i+n}makeSavingThrow(e,t,a,i=!1){const n=t.toLowerCase(),r=e.character.ability_modifiers[n]||0,c=e.character.saving_throws[n]?e.character.proficiency_bonus:0;let u;return i?u=(this.diceRoller?this.diceRoller.rollWithDisadvantage():U.rollWithDisadvantage()).result+r+c:u=this.diceRoller?this.diceRoller.rollSavingThrow(r,c):U.rollSavingThrow(r,c),u>=a}getSpellSlotInfo(e){if(!e.spellSlots)return"No spell slots available";const t=[];for(let a=1;a<=9;a++){const i=e.spellSlots[a]||0;i>0&&t.push(`Level ${a}: ${i} slots`)}return t.length>0?t.join(", "):"No spell slots available"}canUpcast(e,t,a){const i=t.level??0;return a<i?!1:this.hasSpellSlot(e,a)}upcastSpell(e,t,a,i){const n=t.level??0;if(i<n)return{success:!1,spellName:t.name,caster:e,targets:a,effectsApplied:[],spellSlotUsed:i,description:`Cannot cast ${t.name} with a lower-level slot`};const r=t.level;t.level=i;const o=this.castSpell(e,t,a);return t.level=r,o}static hasSpellTag(e,t){return Array.isArray(e.tags)&&e.tags.includes(t)}static getSpellTags(e){return Array.isArray(e.tags)?[...e.tags]:[]}static isDamageSpell(e){return V.hasSpellTag(e,"damage")||!!(e.damage_dice||e.damage)}static requiresConcentration(e){return e.concentration===!0}static isAOESpell(e){return V.hasSpellTag(e,"aoe")}static isMultiTargetSpell(e){return V.hasSpellTag(e,"multi-target")}static isBonusActionSpell(e){return V.hasSpellTag(e,"bonus-action")}static isAllySpell(e){return V.hasSpellTag(e,"ally")}static isSelfSpell(e){return V.hasSpellTag(e,"self")}static getStatusEffectTags(){return di}}class gs{constructor(e){this.config=e}getStyleForCombatant(e){return this.config.overrides?.has(e.id)?this.config.overrides.get(e.id):e.id.startsWith("player")?this.config.playerStyle:this.config.enemyStyle}getSide(e){return e.id.startsWith("player")?"player":"enemy"}getEnemies(e,t){const i=this.getSide(e)==="player"?"enemy":"player";return t.combatants.filter(n=>n.id.startsWith(i)&&!n.isDefeated)}getAllies(e,t){const i=this.getSide(e)==="player"?"player":"enemy";return t.combatants.filter(n=>n.id.startsWith(i)&&!n.isDefeated)}assessThreat(e,t){const a=e.character.hp.max,i=a>0?e.currentHP/a:0,n=this.getAllies(e,t),r=this.getEnemies(e,t),o=n.reduce((v,w)=>{if(w.id===e.id)return v;const E=w.character.hp.max,I=E>0?w.currentHP/E:0;return Math.min(v,I)},1),l=r.reduce((v,w)=>Math.min(v,w.currentHP),r.length>0?1/0:0),c=r.reduce((v,w)=>{const E=w.character.equipment?.weapons.filter(D=>D.equipped)||[];let I=5;for(const D of E){const x=Ze[D.name],N=D.damage_dice||(typeof D.damage!="string"?D.damage?.dice:void 0)||x?.damage?.dice||"1d6",$=this.averageDamageFromFormula(N);$>I&&(I=$)}return Math.max(v,I)},0),d=(e.character.equipment?.items||[]).some(v=>v.quantity>0&&!v.equipped&&v.tags?.some(w=>w==="healing"||w==="consumable")),h=e.spellSlots,p=h!==void 0&&Object.values(h).some(v=>v>0),g=(e.legendaryActionsRemaining??0)>0,m=(e.legendaryResistancesRemaining??0)>0,f=g||m;return{myHPPercent:i,myAC:e.character.armor_class,lowestAllyHPPercent:o,lowestEnemyHP:l,highestEnemyDamage:c,partySize:n.length,enemyCount:r.length,roundNumber:t.roundNumber,isLowHP:i<.25,isCriticalHP:i<.1,hasHealingItems:d,hasSpellSlots:p,hasRemainingLimitedAbilities:f}}decide(e,t){const a=this.getStyleForCombatant(e),i=this.assessThreat(e,t);if(this.getEnemies(e,t).length===0)return{action:"skip",reasoning:"No valid targets remaining"};const r=this.selectSpellAction(e,t,i,a);if(r)return r;const o=this.selectItemAction(e,i,a);if(o)return o;const l=this.selectDefensiveAction(e,t,i,a);return l||this.selectAttackAction(e,t,i,a)}selectSpellAction(e,t,a,i){const n=this.getAvailableSpells(e);if(n.length===0)return null;const r=n.map(d=>this.evaluateSpell(d,e,t,a)),o=r.filter(d=>d.expectedDamage>0&&!d.isHealing),l=r.filter(d=>d.isHealing),c=r.filter(d=>d.isControl&&!d.isHealing),u=r.filter(d=>d.isBuff&&!d.isHealing);if(l.length>0&&this.shouldHeal(e,a,i)){const d=this.pickHealingSpell(l,e,t,a,i);if(d){const h=this.pickHealingTarget(e,t,i);return{action:"castSpell",spellName:d.spell.name,target:h?.id,targetIds:d.isMultiTarget?this.getHealingTargets(e,t,i).map(p=>p.id):void 0,reasoning:`Cast ${d.spell.name} to heal ${h?.character.name??"self"} (${i} style)`}}}if(o.length>0&&this.shouldCastDamageSpell(o,a,i)){const d=this.pickDamageSpell(o,a,i),h=this.pickSpellTargets(d,e,t,i);if(h.length>0)return{action:"castSpell",spellName:d.spell.name,target:h.length===1?h[0].id:void 0,targetIds:h.length>1?h.map(p=>p.id):void 0,reasoning:`Cast ${d.spell.name} on ${h.map(p=>p.character.name).join(", ")} (${i} style, ~${d.expectedDamage.toFixed(1)} expected damage)`}}if(c.length>0&&i==="normal"&&this.shouldCastControlSpell(a)){const d=c[0],h=this.pickSpellTargets(d,e,t,i);if(h.length>0)return{action:"castSpell",spellName:d.spell.name,target:h[0].id,reasoning:`Cast ${d.spell.name} for crowd control (${i} style)`}}if(u.length>0&&i==="normal"&&!a.isLowHP&&!a.isCriticalHP){const d=u[0],h=this.pickBuffTarget(e,t);return{action:"castSpell",spellName:d.spell.name,target:h?.id,reasoning:`Cast ${d.spell.name} on ${h?.character.name??"self"} (buff, normal style)`}}return null}getAvailableSpells(e){const t=e.character.combat_spells;return!t||t.length===0?[]:t.filter(a=>{if(!a.level||a.level===0)return!0;const i=e.spellSlots;return i?this.hasSlotForLevel(i,a.level):!1})}hasSlotForLevel(e,t){if((e[t]??0)>0)return!0;for(let a=t+1;a<=9;a++)if((e[a]??0)>0)return!0;return!1}evaluateSpell(e,t,a,i){const n=!e.level||e.level===0,r=n?0:this.getBestAvailableSlot(t.spellSlots,e.level??0),o=e.damage_dice||e.damage;let l=o?this.averageDamageFromFormula(o):0;const c=V.isAOESpell(e),u=V.isMultiTargetSpell(e),d=V.isAllySpell(e)||V.isSelfSpell(e)||e.tags?.includes("healing"),h=V.hasSpellTag(e,"buff"),p=V.hasSpellTag(e,"control")||V.hasSpellTag(e,"debuff");if((c||u)&&l>0){const g=i.enemyCount;g>1&&(l*=Math.min(g,4))}return!n&&l>0&&(l*=1.5),{spell:e,expectedDamage:l,isHealing:!!d,isBuff:!!h,isControl:!!p,isAOE:c,isMultiTarget:u,isAllySpell:V.isAllySpell(e),isSelfSpell:V.isSelfSpell(e),isBonusAction:V.isBonusActionSpell(e),requiresConcentration:V.requiresConcentration(e),slotLevel:r,isCantrip:n}}getBestAvailableSlot(e,t){if(!e)return 0;if((e[t]??0)>0)return t;for(let a=t+1;a<=9;a++)if((e[a]??0)>0)return a;return 0}shouldHeal(e,t,a){return a==="aggressive"?t.myHPPercent<.75:t.isLowHP||t.isCriticalHP}pickHealingSpell(e,t,a,i,n){if(e.length===0)return null;const r=[...e].sort((o,l)=>l.expectedDamage-o.expectedDamage);if(n==="aggressive")return r[0];if(!i.isCriticalHP){const o=r.find(l=>l.isCantrip);if(o)return o}return r[0]}pickHealingTarget(e,t,a){const i=this.getAllies(e,t);if(i.length===0)return null;const n=[...i].sort((r,o)=>{const l=r.currentHP/r.character.hp.max,c=o.currentHP/o.character.hp.max;return l-c});return a==="normal"&&n[0].id!==e.id?n[0].currentHP/n[0].character.hp.max<.5?n[0]:e:n[0]}getHealingTargets(e,t,a){const i=this.getAllies(e,t),n=a==="aggressive"?1:.75;return i.filter(r=>r.currentHP/r.character.hp.max<n)}shouldCastDamageSpell(e,t,a){if(e.length===0)return!1;if(a==="aggressive")return!0;const i=e[0];return!!(e.some(r=>r.isAOE||r.isMultiTarget)&&t.enemyCount>=3||i.isCantrip||i.expectedDamage>15&&!i.isCantrip)}pickDamageSpell(e,t,a){const i=[...e].sort((o,l)=>l.expectedDamage-o.expectedDamage);if(a==="aggressive")return i[0];const n=i.filter(o=>o.isCantrip),r=i.filter(o=>!o.isCantrip);if(n.length>0){const o=n[0];return r.length>0&&r[0].expectedDamage>o.expectedDamage*1.5&&(t.roundNumber>1||this.totalSlotsRemaining(t)>3)?r[0]:o}return i[0]}totalSlotsRemaining(e){return e.hasSpellSlots?5:0}pickSpellTargets(e,t,a,i){const n=this.getEnemies(t,a),r=this.getAllies(t,a);return e.isAllySpell||e.isHealing?r.length>0?[this.pickHealingTarget(t,a,i)??r[0]]:[]:e.isSelfSpell?[t]:e.isMultiTarget||e.isAOE?n:[this.selectTarget(n,i)]}shouldCastControlSpell(e){return e.enemyCount>=2}pickBuffTarget(e,t){return this.getAllies(e,t).reduce((i,n)=>{const r=Math.max(i.character.ability_scores.STR,i.character.ability_scores.DEX);return Math.max(n.character.ability_scores.STR,n.character.ability_scores.DEX)>r?n:i})}selectItemAction(e,t,a){if(!t.hasHealingItems)return null;const n=(e.character.equipment?.items||[]).filter(r=>r.quantity>0&&!r.equipped);if(n.length===0)return null;if(a==="aggressive"){if(t.myHPPercent<.75&&!t.hasSpellSlots)return{action:"useItem",itemName:n[0].name,target:e.id,reasoning:`Use ${n[0].name} to heal (aggressive: maintain HP for damage output)`}}else if(t.isLowHP&&!t.hasSpellSlots)return{action:"useItem",itemName:n[0].name,target:e.id,reasoning:`Use ${n[0].name} to heal (no spell slots available)`};return null}selectDefensiveAction(e,t,a,i){return i==="aggressive"?null:a.isLowHP&&a.partySize<=1&&a.enemyCount>1?{action:"dodge",reasoning:"Dodge — isolated and low HP, improving survivability"}:null}selectAttackAction(e,t,a,i){const n=this.getEnemies(e,t),r=this.selectTarget(n,i),o=this.selectWeapon(e,i);return{action:"attack",target:r.id,weaponName:o.name,reasoning:`Attack ${r.character.name} with ${o.name} (${i} style, expected ~${o.expectedDamage.toFixed(1)} damage)`}}selectTarget(e,t){if(e.length===0)throw new Error("No valid targets to select from");return e.length===1?e[0]:t==="aggressive"?e.reduce((a,i)=>i.currentHP<a.currentHP?i:a):e.reduce((a,i)=>i.character.armor_class<a.character.armor_class?i:a)}selectWeapon(e,t){const a=this.evaluateWeapons(e);return a.length===0?{name:"Unarmed Strike",expectedDamage:1,attackBonus:0,type:"melee",properties:[]}:a.length===1?a[0]:t==="aggressive"?a.reduce((i,n)=>n.expectedDamage>i.expectedDamage?n:i):a.reduce((i,n)=>{const r=i.expectedDamage+i.attackBonus*.1;return n.expectedDamage+n.attackBonus*.1>r?n:i})}evaluateWeapons(e){const t=[],a=e.character.equipment?.weapons.filter(n=>n.equipped)||[];for(const n of a){const r=Ze[n.name],o=n.damage_dice||(typeof n.damage!="string"?n.damage?.dice:void 0)||r?.damage?.dice||"1d6",l=this.averageDamageFromFormula(o),c=n.weaponProperties?.includes("ranged")||r?.weaponProperties?.includes("ranged")||!1,u=c?"DEX":"STR",h=Math.floor((e.character.ability_scores[u]-10)/2)+e.character.proficiency_bonus;t.push({name:n.name,expectedDamage:l,attackBonus:h,type:c?"ranged":"melee",properties:n.weaponProperties||r?.weaponProperties||[]})}const i=Math.floor((e.character.ability_scores.STR-10)/2);return t.push({name:"Unarmed Strike",expectedDamage:1+Math.max(0,i),attackBonus:i+e.character.proficiency_bonus,type:"melee",properties:[]}),t}selectLegendaryAction(e,t){const a=e.character.legendary_config;if(!a)return null;const i=e.legendaryActionsRemaining??0;if(i<=0)return null;const n=this.getStyleForCombatant(e),r=a.actions.filter(p=>p.cost<=i);if(r.length===0)return null;const o=this.getEnemies(e,t);if(o.length===0)return null;const l=r.filter(p=>p.damage||p.tags?.includes("damage")),c=r.filter(p=>p.tags?.includes("heal")),u=r.filter(p=>p.tags?.includes("control")),d=this.assessThreat(e,t);if(n==="aggressive"&&l.length>0){const g=[...l].sort((f,v)=>v.cost-f.cost)[0],m=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:g.id,target:m.id,reasoning:`Legendary action: ${g.name} (cost: ${g.cost}, aggressive: max damage)`}}if(l.length>0){const g=[...l].sort((f,v)=>f.cost-v.cost)[0],m=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:g.id,target:m.id,reasoning:`Legendary action: ${g.name} (cost: ${g.cost}, normal: conserve points)`}}if(c.length>0&&d.isLowHP)return{action:"legendaryAction",legendaryActionId:c[0].id,reasoning:`Legendary action: ${c[0].name} (healing, low HP)`};if(u.length>0){const p=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:u[0].id,target:p.id,reasoning:`Legendary action: ${u[0].name} (control)`}}const h=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:r[0].id,target:h.id,reasoning:`Legendary action: ${r[0].name} (fallback)`}}averageDamageFromFormula(e){if(!e)return 0;const a=e.replace(/\s/g,"").match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!a)return 0;const i=parseInt(a[1],10),n=parseInt(a[2],10),r=a[3]?parseInt(a[3],10):0,o=(n+1)/2;return i*o+r}isSupportArchetype(e){const t=e.character.combat_spells;if(!t||t.length===0)return!1;const a=t.some(n=>V.isAllySpell(n)||n.tags?.includes("healing")),i=t.some(n=>V.hasSpellTag(n,"buff"));return a||i}}class fs{constructor(e){this.diceRoller=e}rollInitiativeForCombatant(e){const t=e.character.ability_modifiers.dexterity??0,a=this.diceRoller?this.diceRoller.rollD20():U.rollD20(),i=a+t;return e.initiative=i,{combatant:e,d20Roll:a,dexModifier:t,initiativeTotal:i}}rollInitiativeForAll(e){const t=[];for(const i of e){const n=this.rollInitiativeForCombatant(i);t.push(n)}const a=[...e].sort((i,n)=>{if(i.initiative!==n.initiative)return n.initiative-i.initiative;const r=i.character.ability_modifiers.dexterity??0;return(n.character.ability_modifiers.dexterity??0)-r});return{results:t,sortedCombatants:a}}getNextCombatant(e,t){const a=(t+1)%e.length,i=a===0;return{combatant:e[a],index:a,isNewRound:i}}getInitiativeOrder(e){return e.map((t,a)=>{const i=t.character.ability_modifiers.dexterity??0;return`${a+1}. ${t.character.name} (Initiative: ${t.initiative}, DEX: ${i})`})}rerollInitiativeForCombatant(e){const t=e.character.ability_modifiers.dexterity??0,a=this.diceRoller?this.diceRoller.rollD20():U.rollD20();return e.initiative=a+t,e.initiative}delayTurn(e,t){const a=e.findIndex(r=>r.id===t);if(a===-1)return e;const i=[...e],[n]=i.splice(a,1);return i.splice(a+1,0,n),i}resortByInitiative(e){return[...e].sort((t,a)=>{if(t.initiative!==a.initiative)return a.initiative-t.initiative;const i=t.character.ability_modifiers.dexterity??0;return(a.character.ability_modifiers.dexterity??0)-i})}}class ue{constructor(e,t="scaled"){this.diceRoller=e,this.hitMode=t}static computeScaledDamage(e,t,a,i,n){const r=Math.max(1,Math.floor(e*2+(t-a)*.3)),o=ue.getWeaponBonus(i);return n?Math.max(1,Math.floor(r*1.5))+o:Math.max(1,r+o)}static computeDamageScale(e,t){if(e>=t)return 1;const a=t-e;return Math.max(.1,1-a*.1)}static computeAttackBonus(e,t,a){const i=t.includes("ranged"),n=t.includes("finesse");return Math.floor(((e[i||n?"DEX":"STR"]??10)-10)/2)+a}resolveAttack(e,t,a){const i=this.rollAttack(e,t,a);let n,r,o="";if(i.isMiss)o=`${e.character.name} uses ${a.name} against ${t.character.name} - CRITICAL MISS (natural 1)!`;else if(!i.hit)o=`${e.character.name} uses ${a.name} against ${t.character.name} - Miss (rolled ${i.d20Roll}, needed ${i.targetAC})`;else{if(n=this.rollDamage(e,t,a,i.isCritical),i.damageScale!==void 0&&i.damageScale<1){const l=Math.max(1,Math.floor(n.total*i.damageScale));n={...n,total:l}}r=t.currentHP-n.total,r<0?(r=0,t.currentHP=0,t.isDefeated=!0):t.currentHP=r,i.isCritical?o=`${e.character.name} uses ${a.name} against ${t.character.name} - CRITICAL HIT! (natural 20, doubled damage: ${n.total})`:o=`${e.character.name} uses ${a.name} against ${t.character.name} - Hit! (rolled ${i.d20Roll}, damage: ${n.total})`}return{attacker:e,target:t,attack:a,attackRoll:i,damageRoll:n,hpAfterDamage:r,description:o}}rollAttack(e,t,a){const i=this.diceRoller?this.diceRoller.rollD20():U.rollD20(),n=a.attack_bonus??0,r=i+n,o=t.character.armor_class,l=this.diceRoller?this.diceRoller.isCriticalHit(i):U.isCriticalHit(i),c=this.diceRoller?this.diceRoller.isCriticalMiss(i):U.isCriticalMiss(i);let u,d;return this.hitMode==="scaled"?(u=!c,!c&&!l?d=ue.computeDamageScale(r,o):d=1):u=!c&&(l||r>=o),{d20Roll:i,attackBonus:n,totalRoll:r,targetAC:o,hit:u,isCritical:l,isMiss:c,damageScale:d}}rollDamage(e,t,a,i){return this.hitMode==="dnd"?this.rollDamageDND(e,t,a,i):this.rollDamageScaled(e,t,a,i)}getDamageModifier(e,t){const a=e.character.ability_modifiers,i=a.STR??0,n=a.DEX??0;return t.type==="spell"?0:t.type==="ranged"?n:t.properties?.includes("finesse")?Math.max(i,n):i}rollDamageDND(e,t,a,i){const n=e.character.ability_scores.STR??10,r=e.character.ability_scores.DEX??10,o=a.properties?.includes("finesse")??!1,l=a.properties?.includes("ranged")??!1,c=Math.floor((n-10)/2),u=Math.floor((r-10)/2),d=o?Math.max(c,u):l?u:c,h=a.damage_dice??"",p=this.diceRoller?this.diceRoller.calculateDamage(h,d,i):U.calculateDamage(h,d,i),g=Math.max(1,p.total);return{diceFormula:h,rolls:p.rolls,modifier:d,total:g,isCritical:p.isCritical,baseDamage:0,weaponRoll:p.total-d}}rollDamageScaled(e,t,a,i){const n=e.character.level??1,r=e.character.ability_scores.STR??10,o=t.character.armor_class,l=Math.max(1,Math.floor(n*2+(r-o)*.3)),c=ue.getWeaponBonus(a.damage_dice),u=ue.computeScaledDamage(n,r,o,a.damage_dice??"",i);return{diceFormula:a.damage_dice??"",rolls:[],modifier:0,total:u,isCritical:i,baseDamage:l,weaponRoll:c}}static getWeaponBonus(e){if(!e)return 0;try{const t=U.parseDiceFormula(e),a=t.diceCount*(t.diceSides+1)/2;return Math.round(a/2.5)}catch{return 0}}static formatWeaponDamage(e,t,a){if(a==="scaled"){const i=ue.getWeaponBonus(e);return i>0?`+${i} ${t}`:t}return`${e} ${t}`}static estimateDamagePerHit(e){if(e.hitMode==="scaled")return ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!1);const t=Math.floor((e.abilityScore-10)/2);try{const a=U.parseDiceFormula(e.damageDice);return a.diceCount*(a.diceSides+1)/2+a.modifier+t}catch{return t+1}}static estimateDPR(e){const t=e.attackCount??1;if(e.hitMode==="scaled"){const l=ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!1),c=ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!0),u=e.proficiencyBonus??Math.ceil(1+((e.level||1)-1)/4),d=e.attackBonus??Math.floor((e.abilityScore-10)/2)+u;let h=0;for(let p=1;p<=20;p++){if(p===1)continue;if(p===20){h+=c;continue}const g=p+d,m=ue.computeDamageScale(g,e.targetAC);h+=Math.max(1,Math.floor(l*m))}return h/20*t}const a=ue.estimateDamagePerHit(e),i=e.proficiencyBonus??Math.ceil(1+((e.level||1)-1)/4),n=Math.floor((e.abilityScore-10)/2),r=e.attackBonus??n+i,o=Math.max(.05,Math.min(.95,(21-(e.targetAC-r))/20));return a*o*t}isInRange(e,t,a){if(!e.position||!t.position)return!0;const i=Math.sqrt(Math.pow(e.position.x-t.position.x,2)+Math.pow(e.position.y-t.position.y,2));return a.range?i<=a.range:i<=5}calculateAttackBonus(e,t,a,i=!1){let n=a;return i&&(n+=e.proficiency_bonus),n}attackWithAdvantage(e,t,a){const i=this.diceRoller?this.diceRoller.rollWithAdvantage():U.rollWithAdvantage(),n=i.roll1,r=i.roll2,o=i.result,l=a.attack_bonus??0,c=o+l,u=t.character.armor_class,d=(this.diceRoller?this.diceRoller.isCriticalHit(n):U.isCriticalHit(n))||(this.diceRoller?this.diceRoller.isCriticalHit(r):U.isCriticalHit(r)),h=this.diceRoller?this.diceRoller.isCriticalMiss(o):U.isCriticalMiss(o);let p,g;if(this.hitMode==="scaled")if(p=!h,!h&&!d&&c<u){const f=u-c;g=Math.max(.1,1-f*.1)}else g=1;else p=!h&&(d||c>=u);const m=`${e.character.name} attacks with advantage (rolled ${n} and ${r}, using ${o})`;if(p&&!h){let f=this.rollDamage(e,t,a,d);if(g!==void 0&&g<1){const w=Math.max(1,Math.floor(f.total*g));f={...f,total:w}}const v=t.currentHP-f.total;return v<0?(t.currentHP=0,t.isDefeated=!0):t.currentHP=v,{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},damageRoll:f,hpAfterDamage:t.currentHP,description:m}}return{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},description:m}}attackWithDisadvantage(e,t,a){const i=this.diceRoller?this.diceRoller.rollWithDisadvantage():U.rollWithDisadvantage(),n=i.roll1,r=i.roll2,o=i.result,l=a.attack_bonus??0,c=o+l,u=t.character.armor_class,d=this.diceRoller?this.diceRoller.isCriticalHit(o):U.isCriticalHit(o),h=(this.diceRoller?this.diceRoller.isCriticalMiss(n):U.isCriticalMiss(n))||(this.diceRoller?this.diceRoller.isCriticalMiss(r):U.isCriticalMiss(r));let p,g;if(this.hitMode==="scaled")if(p=!h,!h&&!d&&c<u){const f=u-c;g=Math.max(.1,1-f*.1)}else g=1;else p=!h&&(d||c>=u);const m=`${e.character.name} attacks with disadvantage (rolled ${n} and ${r}, using ${o})`;if(p&&!h){let f=this.rollDamage(e,t,a,d);if(g!==void 0&&g<1){const w=Math.max(1,Math.floor(f.total*g));f={...f,total:w}}const v=t.currentHP-f.total;return v<0?(t.currentHP=0,t.isDefeated=!0):t.currentHP=v,{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},damageRoll:f,hpAfterDamage:t.currentHP,description:m}}return{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},description:m}}static simulateAttacks(e,t,a,i=1e3,n,r){const o=new ue(n,r),l=new Map;let c=0,u=0,d=0;for(let g=0;g<i;g++){const m={id:`sim-atk-${g}`,character:e,initiative:0,currentHP:e.hp.max,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1},f={id:`sim-tgt-${g}`,character:t,initiative:0,currentHP:t.hp.max,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1},v=o.resolveAttack(m,f,a),w=v.damageRoll?.total??0;l.set(w,(l.get(w)??0)+1),v.attackRoll.isMiss||!v.attackRoll.hit?d++:(c++,v.attackRoll.isCritical&&u++)}const h=Array.from(l.entries()).map(([g,m])=>({damage:g,count:m,percentage:m/i*100})).sort((g,m)=>g.damage-m.damage),p=h.reduce((g,m)=>g+m.damage*m.count,0)/i;return{iterations:i,totalHits:c,totalMisses:d,totalCrits:u,hitRate:c/i*100,critRate:u/i*100,missRate:d/i*100,averageDamage:p,maxDamage:Math.max(...l.keys()),distribution:h}}}class Se{constructor(e){this.seed=e,this.counter=0}random(){const e=y.deriveSeed(this.seed,this.counter.toString());return this.counter++,y.hashSeedToFloat(e)}randomInt(e,t){return Math.floor(this.random()*(t-e))+e}randomChoice(e){const t=this.randomInt(0,e.length);return e[t]}weightedChoice(e){const t=e.reduce((i,[,n])=>i+n,0);let a=this.random()*t;for(const[i,n]of e)if(a-=n,a<=0)return i;return e[e.length-1][0]}shuffle(e){const t=[...e];for(let a=t.length-1;a>0;a--){const i=this.randomInt(0,a+1);[t[a],t[i]]=[t[i],t[a]]}return t}reset(){this.counter=0}}const ys={1:{easy:25,medium:50,hard:75,deadly:100},2:{easy:50,medium:100,hard:150,deadly:200},3:{easy:75,medium:150,hard:225,deadly:300},4:{easy:125,medium:250,hard:375,deadly:500},5:{easy:250,medium:500,hard:750,deadly:1e3},6:{easy:300,medium:600,hard:900,deadly:1200},7:{easy:350,medium:750,hard:1100,deadly:1700},8:{easy:400,medium:800,hard:1200,deadly:1900},9:{easy:500,medium:1e3,hard:1500,deadly:2300},10:{easy:600,medium:1200,hard:1800,deadly:2400},11:{easy:800,medium:1600,hard:2400,deadly:3600},12:{easy:1e3,medium:2e3,hard:3e3,deadly:4500},13:{easy:1200,medium:2400,hard:3600,deadly:5400},14:{easy:1400,medium:2800,hard:4200,deadly:5700},15:{easy:1600,medium:3200,hard:4800,deadly:6400},16:{easy:2e3,medium:4e3,hard:6e3,deadly:8e3},17:{easy:2400,medium:4800,hard:7200,deadly:9600},18:{easy:3e3,medium:6e3,hard:9e3,deadly:12e3},19:{easy:4e3,medium:8e3,hard:12e3,deadly:16e3},20:{easy:5e3,medium:1e4,hard:15e3,deadly:2e4}},Mt={0:10,.125:25,.25:50,.5:100,1:200,2:450,3:700,4:1100,5:1800,6:2300,7:2900,8:3900,9:5e3,10:5900,11:7200,12:8400,13:1e4,14:11500,15:13e3,16:15e3,17:18e3,18:2e4,19:22e3,20:25e3,21:33e3,22:41e3,23:5e4,24:62e3,26:9e4,27:105e3,28:12e4,29:135e3,30:155e3},na={1:1,2:1.5,3:2,4:2,5:2,6:2,7:1.5,8:1.5,9:1.5,10:1.5,11:1,12:1,13:1,14:1,15:1},sr={DEFAULT:1,EASIER:.9,MUCH_EASIER:.8,HARDER:1.1,MUCH_HARDER:1.2,BRUTAL:1.5};function vs(s,e){const t=ys[s];return t&&t[e]||0}function Wt(s,e){return s.reduce((t,a)=>t+vs(a,e),0)}function ka(s){return Mt[s]||0}function Ta(s){if(s<=0)return 0;const e=Object.keys(Mt).map(Number).sort((n,r)=>n-r);let t=0,a=e.length-1,i=0;for(;t<=a;){const n=Math.floor((t+a)/2),r=e[n];Mt[r]<=s?(i=r,t=n+1):a=n-1}return i}function nr(s,e=1){return Math.round(s*e)}function ot(s){return s<=0?1:s>=15?na[15]:na[s]||1}function rr(s,e){const t=s.reduce((i,n)=>i+ka(n),0),a=e!==void 0?e:ot(s.length);return Math.round(t*a)}function bs(s){if(s.length===0)return 1;const e=s.reduce((t,a)=>t+a,0);return Math.floor(e/s.length)}function or(s){return typeof s=="string"&&["easy","medium","hard","deadly"].includes(s)}class ws{constructor(e={},t){this.diceRoller=t,this.initiativeRoller=new fs(t),this.attackResolver=new ue(t,e.hitMode),this.spellCaster=new V(t),this.config={useEnvironment:!0,useMusic:!1,tacticalMode:!1,maxTurnsBeforeDraw:100,allowFleeing:!1,seed:e.seed||`combat_${Date.now()}`,...e},this.rng=new Se(this.config.seed)}startCombat(e,t,a){const i=[];let n=0;for(const l of e)i.push(this.createCombatant(l,`player_${n++}`));for(const l of t)i.push(this.createCombatant(l,`enemy_${n++}`));const r=this.initiativeRoller.rollInitiativeForAll(i);return{id:`combat_${Date.now()}`,combatants:r.sortedCombatants,currentTurnIndex:0,roundNumber:1,environment:a,history:[],isActive:!0,startTime:Date.now(),lastUpdated:Date.now()}}getCurrentCombatant(e){return e.combatants[e.currentTurnIndex]}executeAttack(e,t,a,i){const n=this.getAttackAdvantageDisadvantage(t,a,i);let r;n==="advantage"?r=this.attackResolver.attackWithAdvantage(t,a,i):n==="disadvantage"?r=this.attackResolver.attackWithDisadvantage(t,a,i):r=this.attackResolver.resolveAttack(t,a,i);const o={type:"attack",actor:t,target:a,attack:i,result:{success:r.attackRoll.hit,roll:r.attackRoll.d20Roll,isCritical:r.attackRoll.isCritical,damage:r.damageRoll?.total,damageType:i.damage_type,targetHP:r.hpAfterDamage,description:r.description}};return e.history.push(o),a.currentHP<=0?(a.isDefeated=!0,a.concentratingOn&&(a.concentratingOn=void 0)):r.attackRoll.hit&&r.damageRoll?.total&&a.concentratingOn&&this.checkConcentration(e,a,r.damageRoll.total),o}executeWeaponAttack(e,t,a,i){const n=t.character.equipment?.weapons.filter(l=>l.equipped)||[];if(i==="unarmed"||n.length===0){const l=this.buildUnarmedAttack(t.character);return this.executeAttack(e,t,a,l)}const r=i?n.find(l=>l.name===i):n[0];if(!r)throw new Error(`Weapon "${i}" is not equipped`);const o=this.buildAttackFromWeapon(r,t.character);return this.executeAttack(e,t,a,o)}buildAttackFromWeapon(e,t){const a=Ze[e.name];if(!a)throw new Error(`Weapon "${e.name}" not found in equipment database`);const i=a.weaponProperties||[],n=i.includes("ranged"),r=t.proficiency_bonus,o=ue.computeAttackBonus(t.ability_scores,i,r);return{name:e.name,damage_dice:e.damage_dice||a.damage?.dice||"1d6",damage_type:a.damage?.damageType||"bludgeoning",type:n?"ranged":"melee",attack_bonus:o,properties:i}}buildUnarmedAttack(e){const t=Math.floor((e.ability_scores.STR-10)/2),a=e.proficiency_bonus;return{name:"Unarmed Strike",damage_dice:"1d1",damage_type:"bludgeoning",type:"melee",attack_bonus:t+a,properties:[]}}executeCastSpell(e,t,a,i){const n=this.spellCaster.castSpell(t,a,i),r={type:"spell",actor:t,targets:i,spell:a,result:{success:n.success,damage:n.damage?.total,damageType:a.damage_type??a.damageType,description:n.description}};if(e.history.push(r),n.success)for(const o of i)for(const l of n.effectsApplied)this.applyStatusEffect(o,l);for(const o of i)o.currentHP<=0?(o.isDefeated=!0,o.concentratingOn&&(o.concentratingOn=void 0)):n.success&&n.damage?.total&&o.concentratingOn&&this.checkConcentration(e,o,n.damage.total);return r}executeLegendaryAction(e,t,a,i){const n=t.character.legendary_config;if(!n)throw new Error(`${t.character.name} has no legendary actions (not a boss)`);const r=n.actions.find(p=>p.id===a.id);if(!r)throw new Error(`Legendary action "${a.id}" not found on ${t.character.name}`);const o=r.cost,l=t.legendaryActionsRemaining??0;if(l<o)throw new Error(`${t.character.name} needs ${o} legendary action points but only has ${l} remaining`);t.legendaryActionsRemaining=l-o;let c,u;if(r.damage&&i){const p=r.damage.replace(/\s/g,""),g=this.diceRoller;g?c=g.parseDiceFormula(p).total:c=U.parseDiceFormula(p).total;const m=this.applyDamage(i,c);u=i.currentHP,i.currentHP<=0?(i.isDefeated=!0,i.concentratingOn&&(i.concentratingOn=void 0)):i.concentratingOn&&m>0&&this.checkConcentration(e,i,m)}const d=i?`${t.character.name} uses ${r.name} on ${i.character.name}${c?` for ${c} ${r.damage_type??""} damage`:""} (${o} action point${o!==1?"s":""} spent, ${t.legendaryActionsRemaining} remaining)`:`${t.character.name} uses ${r.name} (${o} action point${o!==1?"s":""} spent, ${t.legendaryActionsRemaining} remaining)`,h={type:"legendaryAction",actor:t,target:i,legendaryAction:{id:r.id,name:r.name,description:r.effect,cost:r.cost,effect:r.effect,damage:r.damage,damageType:r.damage_type,archetypes:[]},result:{success:!0,damage:c,damageType:r.damage_type,targetHP:u,description:d}};return e.history.push(h),i?.isDefeated&&this.checkCombatStatus(e),h}useLegendaryResistance(e,t){const a=t.legendaryResistancesRemaining??0;return a<=0?!1:(t.legendaryResistancesRemaining=a-1,e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name} used legendary resistance (${t.legendaryResistancesRemaining} remaining today)`}}),!0)}executeDodge(e,t){const a={type:"dodge",actor:t,result:{success:!0,description:`${t.character.name} takes the Dodge action (AC increased until next turn)`}};return e.history.push(a),a}executeDash(e,t){const a={type:"dash",actor:t,result:{success:!0,description:`${t.character.name} takes the Dash action (double movement)`}};return e.history.push(a),a}executeDisengage(e,t){const a={type:"disengage",actor:t,result:{success:!0,description:`${t.character.name} takes the Disengage action (no opportunity attacks provoked)`}};return e.history.push(a),a}canFlee(){return this.config.allowFleeing===!0}executeFlee(e,t){if(!this.canFlee())throw new Error("Fleeing is not allowed in this combat. Enable with allowFleeing: true in CombatConfig.");const a={type:"flee",actor:t,result:{success:!0,description:`${t.character.name} flees from combat!`}};e.history.push(a);const i=e.combatants.findIndex(n=>n.id===t.id);return i!==-1&&(e.combatants.splice(i,1),i<e.currentTurnIndex?e.currentTurnIndex--:i===e.currentTurnIndex&&(e.currentTurnIndex=Math.min(e.currentTurnIndex,e.combatants.length-1))),t.isDefeated=!0,e.lastUpdated=Date.now(),this.checkCombatStatus(e),a}nextTurn(e){const t=e.combatants[e.currentTurnIndex];t.actionUsed=!1,t.bonusActionUsed=!1,t.reactionUsed=!1;const a=(this.config.maxTurnsBeforeDraw||100)+e.combatants.length;for(let i=0;i<a;i++){const n=(e.currentTurnIndex+1)%e.combatants.length;if(n===0){e.roundNumber++;for(const l of e.combatants)l.character.legendary_config&&!l.isDefeated&&(l.legendaryActionsRemaining=3)}e.currentTurnIndex=n,e.lastUpdated=Date.now();const o=e.combatants[n];if(!o.isDefeated){if(this.processStartOfTurnDamage(e,o),o.isDefeated){if(this.checkCombatStatus(e),!e.isActive)return e;continue}if(this.shouldSkipTurn(o)){if(o.actionUsed=!0,o.bonusActionUsed=!0,o.reactionUsed=!0,o.concentratingOn){const c=this.dropConcentration(o,"Incapacitated");c&&e.history.push({type:"statusEffectTick",actor:o,result:{success:!0,description:`${o.character.name} lost concentration on ${c.name} (incapacitated)`}})}const l=o.statusEffects.filter(c=>c.mechanicalEffects?.skipTurn).map(c=>c.name);if(e.history.push({type:"statusEffectTick",actor:o,result:{success:!0,description:`${o.character.name}'s turn is skipped (${l.join(", ")})`}}),this.tickStatusEffects(e,o),this.checkCombatStatus(e),!e.isActive)return e;continue}this.tickStatusEffects(e,o);break}}return this.checkCombatStatus(e),e}processStartOfTurnDamage(e,t){for(const a of t.statusEffects)if(a.damage&&a.damage>0){const i=this.applyDamage(t,a.damage);e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,damage:i,damageType:a.damageType,description:`${t.character.name} takes ${i} ${a.damageType??""} damage from ${a.name}`}}),t.concentratingOn&&(t.isDefeated?t.concentratingOn=void 0:this.checkConcentration(e,t,i))}}tickStatusEffects(e,t){if(t.statusEffects.length===0)return;for(const i of t.statusEffects)i.duration--;const a=this.removeExpiredStatusEffects(t);if(a.length>0){const i=a.map(n=>n.name).join(", ");e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name}: ${i} expired`}})}}getAttackAdvantageDisadvantage(e,t,a){let i=!1,n=!1;const r=a.type??"melee";for(const o of e.statusEffects)o.mechanicalEffects&&(o.mechanicalEffects.disadvantageOnAttack&&(n=!0),o.mechanicalEffects.disadvantageOnAttackNonSource&&o.source!==t.id&&(n=!0));for(const o of t.statusEffects)o.mechanicalEffects&&(o.mechanicalEffects.advantageOnMeleeAttackAgainst&&r==="melee"&&(i=!0),o.mechanicalEffects.advantageOnRangedAttackAgainst&&r==="ranged"&&(i=!0));return i&&n?"normal":i?"advantage":n?"disadvantage":"normal"}shouldSkipTurn(e){return e.statusEffects.some(t=>t.mechanicalEffects?.skipTurn)}checkCombatStatus(e){const t=e.combatants.filter(o=>o.id.startsWith("player")),a=e.combatants.filter(o=>o.id.startsWith("enemy")),i=a.every(o=>o.isDefeated),n=t.every(o=>o.isDefeated),r=e.history.length>=(this.config.maxTurnsBeforeDraw||100);(i||n||r)&&(e.isActive=!1,i&&!n?(e.winner=t.find(o=>!o.isDefeated),e.winnerSide="player"):n&&!i?(e.winner=a.find(o=>!o.isDefeated),e.winnerSide="enemy"):e.winnerSide="draw")}getCombatResult(e){if(e.isActive)return null;const t=e.combatants.filter(c=>c.id.startsWith("enemy")),a=e.combatants.filter(c=>c.isDefeated);let i=0;i=Math.ceil(e.history.length/Math.max(1,e.combatants.length));const n=t.filter(c=>c.isDefeated).reduce((c,u)=>{const d=u.character.cr??u.character.level;return c+ka(d)},0),r=e.winner?`${e.winner.character.name} won the combat!`:"Combat ended in a draw.";let o=0,l=[];if(this.config.treasure){if(typeof this.config.treasure.gold=="number")o=this.config.treasure.gold;else if(typeof this.config.treasure.gold=="object"&&this.config.treasure.gold!==null){const{min:c,max:u}=this.config.treasure.gold;o=c+Math.floor(this.rng.random()*(u-c+1))}l=this.config.treasure.items||[]}else o=Math.floor(this.rng.random()*100);return{winner:e.winner,winnerSide:e.winnerSide??"draw",defeated:a,roundsElapsed:e.roundNumber,totalTurns:i,xpAwarded:n,treasureAwarded:{gold:o,items:l},description:r}}createCombatant(e,t){const a={id:t,character:e,initiative:0,currentHP:e.hp.max,temporaryHP:0,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1,spellSlots:this.initializeSpellSlots(e)};return e.legendary_config&&(a.legendaryActionsRemaining=3,a.legendaryResistancesRemaining=e.legendary_config.resistances_per_day),a}validateSpellSlots(e){const t=[],a=e.spells?.spell_slots;if(!a||Object.keys(a).length===0)return t;for(const[i,n]of Object.entries(a)){const r=Number(i);if(!Number.isInteger(r)||r<1||r>9){t.push({message:`Invalid spell level key "${i}" — must be an integer 1-9`,level:Number.isNaN(r)?void 0:r,severity:"error"});continue}if(typeof n!="object"||n===null){t.push({message:`Spell level ${r} has invalid slot data (expected { total, used }, got ${typeof n})`,level:r,severity:"error"});continue}const{total:o,used:l}=n;if(typeof o!="number"||typeof l!="number"){t.push({message:`Spell level ${r} has non-numeric total (${typeof o}) or used (${typeof l})`,level:r,severity:"error"});continue}o<0&&t.push({message:`Spell level ${r} has negative total (${o})`,level:r,severity:"error"}),l<0&&t.push({message:`Spell level ${r} has negative used (${l})`,level:r,severity:"error"}),o===0&&l!==0&&t.push({message:`Spell level ${r} has total=0 but used=${l} — no slots exist to consume`,level:r,severity:"warn"}),l>o&&t.push({message:`Spell level ${r} has used (${l}) exceeding total (${o})`,level:r,severity:"error"})}return t}validateCombatantSpellSlots(e){const t=[],a=e.character,i=a.spells?.spell_slots,n=e.spellSlots;if(!i||Object.keys(i).length===0){if(n&&Object.keys(n).length>0){const r=sa(a.level),o=Object.keys(r).sort(),l=Object.keys(n).sort();if(o.join(",")!==l.join(","))t.push({message:`Combatant has spell slots ${l.join(", ")} but fallback table for ${a.class} level ${a.level} expects ${o.join(", ")}`,severity:"warn"});else for(const c of o){const u=Number(c);n[u]!==r[u]&&t.push({message:`Spell level ${u}: combatant has ${n[u]} but fallback table expects ${r[u]}`,level:u,severity:"warn"})}}return t}if(!n)return Object.values(i).every(o=>o.total-o.used<=0)||t.push({message:"Character has spell slot data but combatant has no spell slots — expected some remaining",severity:"error"}),t;for(const[r,o]of Object.entries(i)){const l=Number(r),c=Math.max(0,o.total-o.used);if(c===0)n[l]!==void 0&&n[l]>0&&t.push({message:`Spell level ${l}: combatant has ${n[l]} slots but source indicates all used (total=${o.total}, used=${o.used})`,level:l,severity:"error"});else{const u=n[l]??0;u!==c&&t.push({message:`Spell level ${l}: combatant has ${u} but source expects ${c} (total=${o.total}, used=${o.used})`,level:l,severity:"error"})}}for(const r of Object.keys(n)){const o=Number(r);o in i||t.push({message:`Combatant has spell level ${o} with ${n[o]} slots but no source data for that level`,level:o,severity:"warn"})}return t}initializeSpellSlots(e){const t=e.spells?.spell_slots;if(t&&Object.keys(t).length>0){const n=this.validateSpellSlots(e).filter(r=>r.severity==="error");if(n.length>0)console.warn(`[CombatEngine] Spell slot validation failed for ${e.name} (${e.class}): `+n.map(r=>r.message).join("; ")+". Falling back to class table.");else{const r={};for(const[o,l]of Object.entries(t)){const c=l.total-l.used;c>0&&(r[Number(o)]=c)}return Object.keys(r).length>0?r:void 0}}if(["Wizard","Cleric","Sorcerer","Bard","Druid","Warlock","Paladin","Ranger"].includes(e.class))return sa(e.level)}getCombatSummary(e){const t=this.getCurrentCombatant(e);return`Round ${e.roundNumber}, Turn: ${t.character.name} (${t.currentHP}/${t.character.hp.max} HP)`}applyDamage(e,t){const a=Math.max(0,t);if(e.temporaryHP){const i=Math.min(e.temporaryHP,a);e.temporaryHP-=i;const n=a-i;e.currentHP-=n}else e.currentHP-=a;return e.currentHP<=0&&(e.currentHP=0,e.isDefeated=!0),a}healCombatant(e,t){const a=Math.min(t,e.character.hp.max-e.currentHP);return e.currentHP+=a,a}applyTemporaryHP(e,t){e.temporaryHP=Math.max(e.temporaryHP||0,t)}applyStatusEffect(e,t){t.hasConcentration&&e.concentratingOn&&this.dropConcentration(e,"New concentration spell cast");const a=e.statusEffects.findIndex(i=>i.name===t.name);if(a!==-1){const i=e.statusEffects[a];return i.duration=Math.max(i.duration,t.duration),t.damage!==void 0&&(i.damage=Math.max(i.damage??0,t.damage)),t.source!==void 0&&(i.source=t.source),t.mechanicalEffects&&(i.mechanicalEffects={...i.mechanicalEffects,...t.mechanicalEffects}),t.hasConcentration&&(i.hasConcentration=!0),t.damageType&&(i.damageType=t.damageType),t.hasConcentration&&(e.concentratingOn=t.name),i}return e.statusEffects.push(t),t.hasConcentration&&(e.concentratingOn=t.name),t}dropConcentration(e,t="Concentration ended"){if(!e.concentratingOn)return;const a=e.concentratingOn,i=e.statusEffects.findIndex(n=>n.name===a);if(i!==-1){const[n]=e.statusEffects.splice(i,1);return e.concentratingOn=void 0,n}e.concentratingOn=void 0}rollConcentrationSave(e,t){if(!e.concentratingOn)return!0;const a=Math.max(10,Math.floor(t/2)),i=e.character.ability_modifiers.CON??0,r=e.character.saving_throws.CON??!1?e.character.proficiency_bonus:0,o=this.diceRoller?this.diceRoller.rollSavingThrow(i,r):U.rollSavingThrow(i,r),l=o-i-r;return l===1?!1:l===20?!0:o>=a}checkConcentration(e,t,a){if(!t.concentratingOn)return!1;if(!this.rollConcentrationSave(t,a)){const n=this.dropConcentration(t,"Took damage and failed concentration save");return n&&e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name} lost concentration on ${n.name} (took ${a} damage)`}}),!0}return!1}removeExpiredStatusEffects(e){const t=e.statusEffects.filter(a=>a.duration<=0);return e.statusEffects=e.statusEffects.filter(a=>a.duration>0),e.concentratingOn&&(e.statusEffects.some(i=>i.name===e.concentratingOn)||(e.concentratingOn=void 0)),t}getLivingCombatants(e){return e.combatants.filter(t=>!t.isDefeated)}getDefeatedCombatants(e){return e.combatants.filter(t=>t.isDefeated)}}class Ss{computeMetrics(e){const t=new Map;for(const o of e.combatants){const l=o.id.startsWith("player")?"player":"enemy";t.set(o.id,{combatantId:o.id,name:o.character.name,side:l,totalDamageDealt:0,totalDamageTaken:0,totalHealingDone:0,spellsCast:0,itemsUsed:0,criticalHits:0,hits:0,misses:0,kills:0,roundsSurvived:0,survived:!o.isDefeated,actionsByType:{},damagePerRound:[],hpRemainingPercent:0})}const a=new Set(["attack","spell","dodge","dash","disengage","flee","help","hide","ready","useItem","skip"]),i=new Map;for(const o of e.history){const l=o.actor.id,c=t.get(l);switch(c&&(c.actionsByType[o.type]=(c.actionsByType[o.type]||0)+1),a.has(o.type)&&i.set(l,(i.get(l)??0)+1),o.type){case"attack":{const u=o.result?.damage??0,d=o.result?.isCritical===!0,h=o.result?.success===!0;if(c&&(h?c.hits+=1:c.misses+=1),c&&u>0&&(c.totalDamageDealt+=u,c.criticalHits+=d?1:0),o.target){const p=t.get(o.target.id);p&&u>0&&(p.totalDamageTaken+=u)}break}case"spell":{const u=o.result?.damage??0,d=o.result?.isCritical===!0,h=this.isHealingSpell(o);if(c)if(c.spellsCast+=1,h){const p=o.targets??(o.target?[o.target]:[]);for(const g of p){const m=o.result?.damage??0;m>0&&(c.totalHealingDone+=m)}}else u>0&&(c.totalDamageDealt+=u,c.criticalHits+=d?1:0,o.result?.success===!0?c.hits+=1:c.misses+=1);if(!h&&u>0){const p=o.targets??(o.target?[o.target]:[]);for(const g of p){const m=t.get(g.id);m&&(m.totalDamageTaken+=u)}}break}case"legendaryAction":{const u=o.result?.damage??0;if(c&&u>0&&(c.totalDamageDealt+=u),o.target&&u>0){const d=t.get(o.target.id);d&&(d.totalDamageTaken+=u)}break}case"useItem":{c&&(c.itemsUsed+=1);break}}}const n=new Map;for(const o of e.history)if((o.type==="attack"||o.type==="spell"||o.type==="legendaryAction")&&o.target){const l=o.result?.damage??0;if(l>0&&n.set(o.target.id,o.actor.id),o.targets)for(const c of o.targets)l>0&&n.set(c.id,o.actor.id)}for(const o of e.combatants)if(o.isDefeated){const l=n.get(o.id);if(l){const c=t.get(l);c&&(c.kills+=1)}}const r=e.roundNumber;for(const[o,l]of t){l.roundsSurvived=Math.max(1,r);const c=i.get(o)??0;c>0&&l.totalDamageDealt>0&&(l.damagePerRound=[l.totalDamageDealt/c]);const u=e.combatants.find(d=>d.id===o);u&&u.character.hp.max>0&&(l.hpRemainingPercent=l.survived?Math.round(u.currentHP/u.character.hp.max*100):0)}return e.metrics=t,t}isHealingSpell(e){if(!e.spell)return!1;const t=e.spell.tags??[];return t.includes("healing")||t.includes("heal")}}class _s{runFullCombat(e,t,a,i,n){const r=new ws(i??{},n),o=new gs(a),l=r.startCombat(e,t);if(l.combatants.length===0)return{combat:l,result:{winnerSide:"draw",defeated:[],roundsElapsed:0,totalTurns:0,xpAwarded:0,description:"No combatants — combat could not start."},metrics:new Map};for(;l.isActive;){const h=r.getCurrentCombatant(l);if(h.isDefeated){r.nextTurn(l);continue}if(this.hasSkipTurnEffect(h)){l.history.push({type:"statusEffectTick",actor:h,result:{success:!0,description:`${h.character.name}'s turn is skipped (${h.statusEffects.filter(g=>g.mechanicalEffects?.skipTurn).map(g=>g.name).join(", ")})`}}),r.nextTurn(l);continue}const p=o.decide(h,l);if(this.executeDecision(r,l,h,p),!l.isActive||(this.processLegendaryActions(r,o,l,h),!l.isActive))break;r.nextTurn(l)}const c=r.getCombatResult(l);if(!c)throw new Error("AICombatRunner: combat ended but getCombatResult returned null");const d=new Ss().computeMetrics(l);return{combat:l,result:c,metrics:d}}executeDecision(e,t,a,i){switch(i.action){case"attack":{const n=this.findCombatant(t,i.target);n&&this.executeWeaponAttack(e,t,a,n,i.weaponName);break}case"castSpell":{const n=this.findSpell(a,i.spellName);if(n){const r=this.resolveTargets(t,i.target,i.targetIds);r.length>0&&e.executeCastSpell(t,a,n,r)}break}case"dodge":e.executeDodge(t,a);break;case"dash":e.executeDash(t,a);break;case"disengage":e.executeDisengage(t,a);break;case"flee":{e.canFlee()?e.executeFlee(t,a):this.fallbackAttack(e,t,a);break}case"useItem":{const n=a.character.equipment?.items||[],r=i.itemName?n.find(o=>o.name===i.itemName&&o.quantity>0):void 0;if(r&&(r.quantity-=1,r.quantity<=0)){const o=n.indexOf(r);o!==-1&&n.splice(o,1)}t.history.push({type:"useItem",actor:a,result:{success:!!r,description:r?`${a.character.name} uses ${i.itemName}`:`${a.character.name} tries to use ${i.itemName??"an item"} — item not found`}});break}case"legendaryAction":{this.executeLegendaryDecision(e,t,a,i);break}case"skip":t.history.push({type:"statusEffectTick",actor:a,result:{success:!0,description:`${a.character.name} skips their turn (${i.reasoning??"no action"})`}});break}}processLegendaryActions(e,t,a,i){for(const n of a.combatants)if(n.character.legendary_config&&!n.isDefeated&&n.id!==i.id){for(;a.isActive&&(n.legendaryActionsRemaining??0)>0;){const r=t.selectLegendaryAction(n,a);if(!r||(this.executeLegendaryDecision(e,a,n,r),!a.isActive))break}if(!a.isActive)break}}executeLegendaryDecision(e,t,a,i){const n=a.character.legendary_config;if(!n||!i.legendaryActionId)return;const r=n.actions.find(l=>l.id===i.legendaryActionId);if(!r)return;const o=i.target?this.findCombatant(t,i.target):void 0;try{e.executeLegendaryAction(t,a,{id:r.id,name:r.name,cost:r.cost,effect:r.effect,damage:r.damage,damage_type:r.damage_type},o)}catch{}}executeWeaponAttack(e,t,a,i,n){const r=n==="Unarmed Strike"?"unarmed":n;try{e.executeWeaponAttack(t,a,i,r)}catch{try{e.executeWeaponAttack(t,a,i,"unarmed")}catch{}}}findCombatant(e,t){if(t)return e.combatants.find(a=>a.id===t)}findSpell(e,t){if(t)return e.character.combat_spells?.find(a=>a.name===t)}resolveTargets(e,t,a){if(a&&a.length>0)return a.map(i=>e.combatants.find(n=>n.id===i)).filter(i=>i!==void 0);if(t){const i=e.combatants.find(n=>n.id===t);return i?[i]:[]}return[]}fallbackAttack(e,t,a){const i=a.id.startsWith("player")?"enemy":"player",n=t.combatants.filter(r=>r.id.startsWith(i)&&!r.isDefeated);n.length>0&&this.executeWeaponAttack(e,t,a,n[0],void 0)}hasSkipTurnEffect(e){return e.statusEffects.some(t=>t.mechanicalEffects?.skipTurn)}}function As(s){return new ks(s)}class ks{constructor(e){this.rng=typeof e=="string"?new Se(e):e}rollDie(e){if(e<1)throw new Error("Die must have at least 1 side");return this.rng.randomInt(1,e+1)}rollMultipleDice(e,t){const a=[];for(let i=0;i<e;i++)a.push(this.rollDie(t));return a}parseDiceFormula(e){const t=e.match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!t)throw new Error(`Invalid dice formula: ${e}`);const a=parseInt(t[1],10),i=parseInt(t[2],10),n=t[3]?parseInt(t[3],10):0,r=this.rollMultipleDice(a,i),o=r.reduce((l,c)=>l+c,0)+n;return{diceCount:a,diceSides:i,modifier:n,rolls:r,total:o}}rollD20(){return this.rollDie(20)}rollWithAdvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.max(e,t)}}rollWithDisadvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.min(e,t)}}rollInitiative(e){return this.rollD20()+e}isCriticalHit(e){return e===20}isCriticalMiss(e){return e===1}doubleDamage(e){return[...e,...e]}calculateDamage(e,t,a=!1){let n=this.parseDiceFormula(e).rolls;a&&(n=this.doubleDamage(n));const r=n.reduce((o,l)=>o+l,0)+t;return{diceFormula:e,rolls:n,modifier:t,total:r,isCritical:a}}rollSavingThrow(e,t=0){return this.rollD20()+e+t}rollAbilityCheck(e,t=0){return this.rollD20()+e+t}rollPercentile(){return this.rollDie(100)}}const lr=[{id:"skeleton",name:"Skeleton",category:"undead",archetype:"archer",signatureAbility:{id:"skeleton_bone_shot",name:"Bone Shot",description:"Fires a sharpened bone projectile that deals piercing damage. The hollow whistle of the bone strikes fear into the living.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["ammo","accuracy"]},baseStats:{STR:12,DEX:14,CON:12,INT:6,WIS:8,CHA:5},baseHP:13,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"zombie",name:"Zombie",category:"undead",archetype:"brute",signatureAbility:{id:"zombie_undead_grip",name:"Undead Grip",description:"A grapple followed by a bite that deals necrotic damage. The zombie's grip is supernaturally strong and unnaturally cold.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["grapple","persistent"]},baseStats:{STR:14,DEX:8,CON:14,INT:3,WIS:6,CHA:5},baseHP:22,baseAC:8,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"wight",name:"Wight",category:"undead",archetype:"brute",signatureAbility:{id:"wight_life_drain",name:"Life Drain",description:"A melee attack that deals necrotic damage and heals the wight for half the damage dealt. The stolen life force sustains its unholy existence.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["lifesteal"]},baseStats:{STR:16,DEX:12,CON:14,INT:10,WIS:12,CHA:14},baseHP:26,baseAC:14,baseSpeed:30,audioPreference:{bass:.3,mid:.6,treble:.2},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"ghost",name:"Ghost",category:"undead",archetype:"support",signatureAbility:{id:"ghost_horrifying_visage",name:"Horrifying Visage",description:"Projects terrifying spectral visage that frightens nearby enemies. Frightened creatures have disadvantage on ability checks and attack rolls.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:30,properties:["debuff","fear","control"]},baseStats:{STR:8,DEX:14,CON:12,INT:12,WIS:14,CHA:16},baseHP:15,baseAC:11,baseSpeed:0,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["acid","cold","fire","lightning","thunder"],immunities:["poison","necrotic"]}}],cr=[{id:"imp",name:"Imp",category:"fiend",archetype:"archer",signatureAbility:{id:"imp_sting",name:"Sting",description:"A poisonous tail strike that deals poison damage and may poison the target. The imp's venom causes necrosis and weakness.",damageDie:"d6",damageType:"poison",attackType:"ranged",range:30,properties:["poison","debuff"]},baseStats:{STR:6,DEX:15,CON:10,INT:11,WIS:10,CHA:12},baseHP:10,baseAC:13,baseSpeed:20,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"quasit",name:"Quasit",category:"fiend",archetype:"support",signatureAbility:{id:"quasit_fear_aura",name:"Fear Aura",description:"Projects an aura of unnatural terror that frightens nearby enemies. Frightened creatures have disadvantage on ability checks.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:20,properties:["debuff","fear","control"]},baseStats:{STR:8,DEX:12,CON:10,INT:10,WIS:11,CHA:12},baseHP:7,baseAC:13,baseSpeed:40,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"lemure",name:"Lemure",category:"fiend",archetype:"brute",signatureAbility:{id:"lemure_hellish_resilience",name:"Hellish Resilience",description:"A slam attack that deals fire damage while simultaneously healing the lemure. Its corrupted flesh regenerates from hellfire.",damageDie:"d6",damageType:"fire",attackType:"melee",properties:["lifesteal","persistent"]},baseStats:{STR:10,DEX:6,CON:12,INT:3,WIS:8,CHA:5},baseHP:13,baseAC:7,baseSpeed:30,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"demon",name:"Demon",category:"fiend",archetype:"brute",signatureAbility:{id:"demon_chaos_claw",name:"Chaos Claw",description:"A claw attack infused with chaotic energy that deals random damage types. Each strike channels the unpredictability of the abyss.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["chaotic","versatile"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:10},baseHP:28,baseAC:13,baseSpeed:30,audioPreference:{bass:.8,mid:.3,treble:.2},resistances:{resistances:["fire","cold"],immunities:["poison"]}}],ur=[{id:"fire-elemental",name:"Fire Elemental",category:"elemental",archetype:"brute",signatureAbility:{id:"fire_elemental_burning_touch",name:"Burning Touch",description:"A searing touch that deals fire damage and applies burning damage over time. The flames continue to burn after impact.",damageDie:"d6",damageType:"fire",attackType:"melee",properties:["ongoing","burn"]},baseStats:{STR:14,DEX:12,CON:16,INT:6,WIS:10,CHA:6},baseHP:26,baseAC:13,baseSpeed:50,audioPreference:{bass:.8,mid:.2,treble:.1},resistances:{immunities:["fire"],resistances:[]}},{id:"earth-elemental",name:"Earth Elemental",category:"elemental",archetype:"brute",signatureAbility:{id:"earth_elemental_earth_slam",name:"Earth Slam",description:"A devastating slam that deals bludgeoning damage in an area and knocks targets prone. The ground cracks under its power.",damageDie:"d8",damageType:"bludgeoning",attackType:"melee",properties:["aoe","prone"]},baseStats:{STR:18,DEX:8,CON:16,INT:6,WIS:10,CHA:6},baseHP:30,baseAC:17,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{immunities:["poison"],resistances:["necrotic"]}},{id:"water-elemental",name:"Water Elemental",category:"elemental",archetype:"support",signatureAbility:{id:"water_elemental_whirlpool",name:"Whirlpool",description:"Creates a swirling vortex that restrains targets and pulls them toward the center. The crushing waves hold enemies in place.",damageDie:"d6",damageType:"cold",attackType:"spell",range:30,properties:["control","restrain","pull"]},baseStats:{STR:14,DEX:14,CON:14,INT:8,WIS:12,CHA:8},baseHP:22,baseAC:14,baseSpeed:30,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{immunities:["cold"],resistances:["fire","necrotic"]}},{id:"air-elemental",name:"Air Elemental",category:"elemental",archetype:"archer",signatureAbility:{id:"air_elemental_wind_blast",name:"Wind Blast",description:"Projects a concentrated gust of wind that pushes targets back and deals force damage. The howling wind disrupts formation.",damageDie:"d6",damageType:"force",attackType:"ranged",range:60,properties:["push","control"]},baseStats:{STR:12,DEX:16,CON:14,INT:8,WIS:12,CHA:8},baseHP:18,baseAC:15,baseSpeed:90,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{immunities:["lightning"],resistances:["thunder"]}}],dr=[{id:"animated-armor",name:"Animated Armor",category:"construct",archetype:"brute",signatureAbility:{id:"animated_armor_slam",name:"Slam",description:"A powerful melee attack that deals force damage. The enchanted armor strikes with supernatural strength.",damageDie:"d6",damageType:"force",attackType:"melee",properties:["force"]},baseStats:{STR:14,DEX:10,CON:14,INT:3,WIS:6,CHA:1},baseHP:18,baseAC:18,baseSpeed:25,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{immunities:["poison","psychic"]}},{id:"golem",name:"Golem",category:"construct",archetype:"brute",signatureAbility:{id:"golem_immutable_form",name:"Immutable Form",description:"A devastating slam that deals bludgeoning damage. The golem is immune to most status conditions and non-magical physical attacks.",damageDie:"d8",damageType:"bludgeoning",attackType:"melee",properties:["status_immunity","nonmagical_resistance"]},baseStats:{STR:18,DEX:8,CON:16,INT:3,WIS:6,CHA:1},baseHP:32,baseAC:15,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:0},resistances:{immunities:["poison","psychic"],resistances:["bludgeoning","piercing","slashing"]}},{id:"flying-sword",name:"Flying Sword",category:"construct",archetype:"archer",signatureAbility:{id:"flying_sword_diving_strike",name:"Diving Strike",description:"A diving attack that deals bonus damage when moving toward the target. The enchanted sword strikes with supernatural precision.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["charge","accuracy"]},baseStats:{STR:12,DEX:16,CON:12,INT:1,WIS:4,CHA:1},baseHP:10,baseAC:17,baseSpeed:50,audioPreference:{bass:.1,mid:.1,treble:.9},resistances:{immunities:["poison","psychic"]}},{id:"shield-guardian",name:"Shield Guardian",category:"construct",archetype:"support",signatureAbility:{id:"shield_guardian_protection_aura",name:"Protection Aura",description:"Projects a protective field that increases armor class of nearby allies. The magical shield deflects attacks meant for others.",damageDie:"d6",damageType:"force",attackType:"spell",range:20,properties:["buff","ally","defense"]},baseStats:{STR:14,DEX:10,CON:16,INT:6,WIS:10,CHA:5},baseHP:22,baseAC:16,baseSpeed:30,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{immunities:["poison","psychic"]}}],hr=[{id:"young-red-dragon",name:"Young Red Dragon",category:"dragon",archetype:"brute",signatureAbility:{id:"young_red_dragon_fire_breath",name:"Fire Breath",description:"Exhales a cone of searing flame that deals fire damage to all creatures in the area. The intense heat can ignite flammable objects.",damageDie:"d8",damageType:"fire",attackType:"ranged",range:30,properties:["aoe","cone","burn"]},baseStats:{STR:19,DEX:12,CON:17,INT:12,WIS:13,CHA:15},baseHP:45,baseAC:18,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{immunities:["fire"],resistances:[]}},{id:"dragon-wyrmling",name:"Dragon Wyrmling",category:"dragon",archetype:"brute",signatureAbility:{id:"dragon_wyrmling_multiattack",name:"Bite + Claw",description:"A combination attack with bite and claws that deals multiple physical attacks. The wyrmling attacks with ferocious hunger.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack"]},baseStats:{STR:15,DEX:12,CON:14,INT:10,WIS:11,CHA:12},baseHP:26,baseAC:17,baseSpeed:30,audioPreference:{bass:.3,mid:.6,treble:.2},resistances:{immunities:[],resistances:["acid"]}},{id:"drake",name:"Drake",category:"dragon",archetype:"brute",signatureAbility:{id:"drake_tail_swipe",name:"Tail Swipe",description:"A powerful tail strike that deals bludgeoning damage and knocks targets back. The force can push enemies away from the drake.",damageDie:"d6",damageType:"bludgeoning",attackType:"melee",properties:["knockback","push"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:10},baseHP:32,baseAC:15,baseSpeed:40,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{immunities:[],resistances:["cold"]}},{id:"young-blue-dragon",name:"Young Blue Dragon",category:"dragon",archetype:"archer",signatureAbility:{id:"young_blue_dragon_lightning_breath",name:"Lightning Breath",description:"Exhales a line of crackling lightning that deals lightning damage to all creatures in its path. The electrical discharge arcs between targets.",damageDie:"d8",damageType:"lightning",attackType:"ranged",range:60,properties:["aoe","line","chain"]},baseStats:{STR:17,DEX:14,CON:15,INT:14,WIS:13,CHA:15},baseHP:38,baseAC:19,baseSpeed:40,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{immunities:["lightning"],resistances:["thunder"]}}],pr=[{id:"owlbear",name:"Owlbear",category:"monstrosity",archetype:"brute",signatureAbility:{id:"owlbear_multiattack",name:"Multiattack",description:"A devastating combination of beak and claws attacks. The owlbear unleashes its primal fury in a flurry of strikes.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack"]},baseStats:{STR:18,DEX:10,CON:14,INT:3,WIS:10,CHA:6},baseHP:37,baseAC:13,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{resistances:[]}},{id:"griffin",name:"Griffin",category:"monstrosity",archetype:"archer",signatureAbility:{id:"griffin_dive_attack",name:"Dive Attack",description:"A high-speed diving attack from above that deals extra piercing damage. The griffin uses its powerful wings to strike with devastating force.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["charge","flying"]},baseStats:{STR:16,DEX:15,CON:14,INT:4,WIS:12,CHA:7},baseHP:30,baseAC:12,baseSpeed:50,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}},{id:"mimic",name:"Mimic",category:"monstrosity",archetype:"brute",signatureAbility:{id:"mimic_adhesive",name:"Adhesive",description:"A pseudopod attack that deals bludgeoning damage and grapples the target. The mimic's adhesive surface traps prey.",damageDie:"d6",damageType:"bludgeoning",attackType:"melee",properties:["grapple","ambush"]},baseStats:{STR:14,DEX:10,CON:12,INT:6,WIS:8,CHA:4},baseHP:21,baseAC:12,baseSpeed:10,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["acid"]}},{id:"basilisk",name:"Basilisk",category:"monstrosity",archetype:"support",signatureAbility:{id:"basilisk_petrifying_gaze",name:"Petrifying Gaze",description:"A gaze attack that can stun targets. Those who meet the basilisk's eyes feel their limbs turning to stone.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:30,properties:["debuff","control","stun"]},baseStats:{STR:14,DEX:10,CON:12,INT:4,WIS:6,CHA:6},baseHP:26,baseAC:14,baseSpeed:20,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{resistances:["poison"]}}],mr=[{id:"orc",name:"Orc",category:"humanoid",archetype:"brute",signatureAbility:{id:"orc_savage_strike",name:"Savage Strike",description:"A powerful melee attack that deals extra damage. The ferocity of the attack embodies primal aggression.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["versatile"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:8},baseHP:15,baseAC:13,baseSpeed:30,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{resistances:["poison"]}},{id:"bandit",name:"Bandit",category:"humanoid",archetype:"brute",signatureAbility:{id:"bandit_cheap_shot",name:"Cheap Shot",description:"A dirty fighting technique that deals bonus damage against surprised or flat-footed opponents.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["finesse"]},baseStats:{STR:13,DEX:14,CON:12,INT:10,WIS:10,CHA:11},baseHP:11,baseAC:12,baseSpeed:30,audioPreference:{bass:.4,mid:.6,treble:.3},resistances:{resistances:[]}},{id:"hunter",name:"Hunter",category:"humanoid",archetype:"archer",signatureAbility:{id:"hunter_precise_shot",name:"Precise Shot",description:"A carefully aimed shot that can ignore half cover. Exceptional accuracy allows targeting vulnerabilities.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:120,properties:["accuracy"]},baseStats:{STR:11,DEX:16,CON:12,INT:12,WIS:14,CHA:10},baseHP:10,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.3,treble:.8},resistances:{resistances:[]}},{id:"goblin-archer",name:"Goblin Archer",category:"humanoid",archetype:"archer",signatureAbility:{id:"goblin_sneaky_shot",name:"Sneaky Shot",description:"A quick shot from hiding that deals bonus damage. Works best when the goblin is unseen.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["finesse","stealth"]},baseStats:{STR:8,DEX:15,CON:10,INT:10,WIS:8,CHA:8},baseHP:7,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}},{id:"shaman",name:"Shaman",category:"humanoid",archetype:"support",signatureAbility:{id:"shaman_spirit_bond",name:"Spirit Bond",description:"Channels spiritual energy to grant nearby allies a damage bonus on their next attack.",damageDie:"d6",damageType:"force",attackType:"spell",range:30,properties:["buff","ally"]},baseStats:{STR:10,DEX:10,CON:12,INT:12,WIS:15,CHA:13},baseHP:9,baseAC:11,baseSpeed:30,audioPreference:{bass:.3,mid:.7,treble:.3},resistances:{resistances:["necrotic"]}},{id:"cultist",name:"Cultist",category:"humanoid",archetype:"support",signatureAbility:{id:"cultist_dark_blessing",name:"Dark Blessing",description:"Invokes dark powers to temporarily increase nearby allies' armor class.",damageDie:"d6",damageType:"necrotic",attackType:"spell",range:20,properties:["buff","ally","defense"]},baseStats:{STR:11,DEX:12,CON:12,INT:13,WIS:11,CHA:14},baseHP:9,baseAC:13,baseSpeed:30,audioPreference:{bass:.3,mid:.7,treble:.2},resistances:{resistances:["necrotic"]}},{id:"bear",name:"Bear",category:"beast",archetype:"brute",signatureAbility:{id:"bear_maul",name:"Maul",description:"A devastating multiattack with claws that can also grapple the target. Raw power embodied.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack","grapple"]},baseStats:{STR:18,DEX:10,CON:16,INT:2,WIS:12,CHA:6},baseHP:34,baseAC:11,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{resistances:["cold"]}},{id:"boar",name:"Boar",category:"beast",archetype:"brute",signatureAbility:{id:"boar_gore_charge",name:"Gore Charge",description:"A ferocious charge that deals bonus damage if the boar moves at least 20 feet before attacking.",damageDie:"d6",damageType:"piercing",attackType:"melee",properties:["charge"]},baseStats:{STR:13,DEX:11,CON:12,INT:2,WIS:8,CHA:4},baseHP:11,baseAC:11,baseSpeed:40,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{resistances:[]}},{id:"giant-spider",name:"Giant Spider",category:"beast",archetype:"archer",signatureAbility:{id:"spider_web_spray",name:"Web Spray",description:"Shoots sticky webbing at a target to restrain them. A restrained creature has speed 0 and can't use reactions.",damageDie:"d6",damageType:"poison",attackType:"ranged",range:60,properties:["control","restrain"]},baseStats:{STR:12,DEX:14,CON:12,INT:2,WIS:12,CHA:4},baseHP:26,baseAC:13,baseSpeed:30,audioPreference:{bass:.2,mid:.3,treble:.8},resistances:{resistances:["poison"]}},{id:"stirge",name:"Stirge",category:"beast",archetype:"archer",signatureAbility:{id:"stirge_blood_drain",name:"Blood Drain",description:"Attaches to target and drains blood, dealing damage and healing the stirge for half the damage dealt.",damageDie:"d6",damageType:"piercing",attackType:"melee",properties:["lifesteal","grapple"]},baseStats:{STR:4,DEX:14,CON:10,INT:1,WIS:6,CHA:2},baseHP:5,baseAC:12,baseSpeed:10,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}}],Xe=[...mr,...lr,...cr,...ur,...dr,...hr,...pr];function gr(s){return Xe.find(e=>e.id===s)}function fr(s){return Xe.filter(e=>e.category===s)}function yr(s){return Xe.filter(e=>e.archetype===s)}const Ca={common:{statMultiplier:1,hpMultiplier:1,signatureDieSize:6,extraAbilityCount:0,hasResistances:!1},uncommon:{statMultiplier:1.08,hpMultiplier:1.3,signatureDieSize:8,extraAbilityCount:1,hasResistances:!1},elite:{statMultiplier:1.15,hpMultiplier:1.7,signatureDieSize:10,extraAbilityCount:2,hasResistances:!0},boss:{statMultiplier:1.25,hpMultiplier:2.2,signatureDieSize:12,extraAbilityCount:3,hasResistances:!0}};function Oe(s){return Ca[s]}function vr(s){return`1d${Ca[s].signatureDieSize}`}function Ts(){return["common","uncommon","elite","boss"]}function br(s){const e=Ts(),t=e.indexOf(s);if(!(t===-1||t===e.length-1))return e[t+1]}function wr(s){return s}const Sr=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],_r=Sr.map(wr);function Cs(s){return s}const Ar=["Barbarian","Bard","Cleric","Druid","Fighter","Monk","Paladin","Ranger","Rogue","Sorcerer","Warlock","Wizard"],Re=Ar.map(Cs);function ra(s){if(typeof s!="string")return!1;if(Re.includes(s))return!0;try{const t=require("../extensions/ExtensionManager.js").ExtensionManager.getInstance();if(t.get("classes")?.includes(s))return!0;const i=t.get("classes.data");if(i&&Array.isArray(i)&&i.map(r=>r.name).includes(s))return!0}catch{}return!1}const lt=["STR","DEX","CON","INT","WIS","CHA"];function Pe(s){return lt.includes(s)}const Is=["http://","https://","/","assets/"];function Es(s){return typeof s!="string"||s.trim()===""?!1:Is.some(e=>s.startsWith(e))}function hi(s,e){return s===void 0?{valid:!0,errors:[]}:typeof s!="string"?{valid:!1,errors:[`${e} must be a string`]}:s.trim()===""?{valid:!1,errors:[`${e} cannot be an empty string`]}:Es(s)?{valid:!0,errors:[]}:{valid:!1,errors:[`${e} must start with one of: ${Is.join(", ")} (got: "${s}")`]}}function Be(s){const e=[],t=hi(s.icon,"icon");t.valid||e.push(...t.errors);const a=hi(s.image,"image");return a.valid||e.push(...a.errors),e}const pi=["passive","active","resource","trigger"],mt=["default","custom"],mi=["stat_bonus","skill_proficiency","ability_unlock","passive_modifier","resource_grant","spell_slot_bonus"],gt=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],ft=["athletics","acrobatics","sleight_of_hand","stealth","arcana","history","investigation","nature","religion","animal_handling","insight","medicine","perception","survival","deception","intimidation","performance","persuasion"],gi=["none","proficient","expertise"];class Fe{static validateClassFeature(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Class feature must be an object"]};const i=e;if(!i.id||typeof i.id!="string"?a.push("Feature must have a valid id (string)"):this.isValidFeatureId(i.id)||a.push(`Feature id must use lowercase_with_underscores format: "${i.id}"`),(!i.name||typeof i.name!="string")&&a.push("Feature must have a valid name (string)"),(!i.description||typeof i.description!="string")&&a.push("Feature must have a description (string)"),!i.type||typeof i.type!="string"?a.push("Feature must have a type (string)"):pi.includes(i.type)||a.push(`Invalid type: "${i.type}". Must be one of: ${pi.join(", ")}`),!i.class||typeof i.class!="string"?a.push("Feature must have a class (string)"):ra(i.class)||a.push(`Invalid class: "${i.class}". Must be one of: ${Re.join(", ")} or a custom class registered via ExtensionManager.`),typeof i.level!="number"?a.push("Feature must have a level (number)"):(i.level<1||i.level>20)&&a.push(`Feature level must be between 1 and 20 (got: ${i.level})`),!i.source||typeof i.source!="string"?a.push("Feature must have a source (string)"):mt.includes(i.source)||a.push(`Invalid source: "${i.source}". Must be one of: ${mt.join(", ")}`),i.tags!==void 0)if(!Array.isArray(i.tags))a.push("Feature tags must be an array");else for(const r of i.tags)typeof r!="string"&&a.push(`Feature tag must be a string (got: ${typeof r})`);i.lore!==void 0&&typeof i.lore!="string"&&a.push("Feature lore must be a string"),i.subrace!==void 0&&typeof i.subrace!="string"&&a.push("Feature subrace must be a string");const n=Be({icon:i.icon,image:i.image});if(a.push(...n.map(r=>`Feature ${r}`)),i.prerequisites!==void 0){const r=this.validatePrerequisites(i.prerequisites,t);r.valid||a.push(...r.errors.map(o=>`Prerequisites: ${o}`))}if(i.effects!==void 0)if(!Array.isArray(i.effects))a.push("Feature effects must be an array");else for(let r=0;r<i.effects.length;r++){const o=this.validateEffect(i.effects[r]);o.valid||a.push(...o.errors.map(l=>`Effect ${r}: ${l}`))}return{valid:a.length===0,errors:a}}static validateRacialTrait(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Racial trait must be an object"]};const i=e;if(!i.id||typeof i.id!="string"?a.push("Trait must have a valid id (string)"):this.isValidFeatureId(i.id)||a.push(`Trait id must use lowercase_with_underscores format: "${i.id}"`),(!i.name||typeof i.name!="string")&&a.push("Trait must have a valid name (string)"),(!i.description||typeof i.description!="string")&&a.push("Trait must have a description (string)"),!i.race||typeof i.race!="string")a.push("Trait must have a race (string)");else{const r=gt.includes(i.race),o=t?.customRaces??[],l=o.includes(i.race);if(!r&&!l){const c=o.length>0?`, ${o.join(", ")}`:"";a.push(`Invalid race: "${i.race}". Must be one of: ${gt.join(",")}${c} or a custom race registered via ExtensionManager.`)}}if(!i.source||typeof i.source!="string"?a.push("Trait must have a source (string)"):mt.includes(i.source)||a.push(`Invalid source: "${i.source}". Must be one of: ${mt.join(", ")}`),i.tags!==void 0)if(!Array.isArray(i.tags))a.push("Trait tags must be an array");else for(const r of i.tags)typeof r!="string"&&a.push(`Trait tag must be a string (got: ${typeof r})`);i.lore!==void 0&&typeof i.lore!="string"&&a.push("Trait lore must be a string"),i.subrace!==void 0&&typeof i.subrace!="string"&&a.push("Trait subrace must be a string");const n=Be({icon:i.icon,image:i.image});if(a.push(...n.map(r=>`Trait ${r}`)),i.prerequisites!==void 0){const r=this.validatePrerequisites(i.prerequisites,t);r.valid||a.push(...r.errors.map(o=>`Prerequisites: ${o}`))}if(i.effects!==void 0)if(!Array.isArray(i.effects))a.push("Trait effects must be an array");else for(let r=0;r<i.effects.length;r++){const o=this.validateEffect(i.effects[r]);o.valid||a.push(...o.errors.map(l=>`Effect ${r}: ${l}`))}return{valid:a.length===0,errors:a}}static validateEffect(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Effect must be an object"]};const a=e;if(!a.type||typeof a.type!="string"?t.push("Effect must have a type (string)"):mi.includes(a.type)||t.push(`Invalid effect type: "${a.type}". Must be one of: ${mi.join(", ")}`),!a.target||typeof a.target!="string")t.push("Effect must have a target (string)");else{const i=a.type,n=a.target;if(!(i==="stat_bonus"&&this.isAbility(n))){if(i==="skill_proficiency"&&!ft.includes(n))t.push(`Invalid skill target: "${n}". Must be one of: ${ft.join(", ")}`);else if(i==="skill_proficiency"){const r=a.value;typeof r=="string"&&!gi.includes(r)&&t.push(`Invalid proficiency level: "${r}". Must be one of: ${gi.join(", ")}`)}}}return a.value===void 0&&t.push("Effect must have a value"),a.condition!==void 0&&typeof a.condition!="string"&&t.push("Effect condition must be a string"),a.description!==void 0&&typeof a.description!="string"&&t.push("Effect description must be a string"),{valid:t.length===0,errors:t}}static validatePrerequisites(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Prerequisites must be an object"]};const i=e;if(i.level!==void 0&&(typeof i.level!="number"?a.push("Prerequisite level must be a number"):(i.level<1||i.level>20)&&a.push(`Prerequisite level must be between 1 and 20 (got: ${i.level})`)),i.abilities!==void 0)if(typeof i.abilities!="object"||Array.isArray(i.abilities)||i.abilities===null)a.push("Prerequisite abilities must be a record");else for(const[n,r]of Object.entries(i.abilities))lt.includes(n)||a.push(`Invalid ability in prerequisites: "${n}". Must be one of: ${lt.join(", ")}`),(typeof r!="number"||r<1||r>20)&&a.push(`Ability score requirement for ${n} must be a number between 1 and 20`);if(i.class!==void 0){if(typeof i.class!="string")a.push("Prerequisite class must be a string");else if(!ra(i.class)){const n=t?.customClasses??[];if(!n.includes(i.class)){const o=n.length>0?`, ${n.join(", ")}`:"";a.push(`Invalid prerequisite class: "${i.class}". Must be one of: ${Re.join(",")}${o} or a custom class registered via ExtensionManager.`)}}}if(i.race!==void 0)if(typeof i.race!="string")a.push("Prerequisite race must be a string");else{const n=gt.includes(i.race),r=t?.customRaces??[],o=r.includes(i.race);if(!n&&!o){const l=r.length>0?`, ${r.join(", ")}`:"";a.push(`Invalid prerequisite race: "${i.race}". Must be one of: ${gt.join(",")}${l} or a custom race registered via ExtensionManager.`)}}if(i.features!==void 0)if(!Array.isArray(i.features))a.push("Prerequisite features must be an array");else for(const n of i.features)typeof n!="string"?a.push("Prerequisite feature IDs must be strings"):this.isValidFeatureId(n)||a.push(`Invalid prerequisite feature ID format: "${n}"`);if(i.subrace!==void 0&&(typeof i.subrace!="string"?a.push("Prerequisite subrace must be a string"):i.subrace.trim()===""&&a.push("Prerequisite subrace cannot be empty")),i.skills!==void 0)if(!Array.isArray(i.skills))a.push("Prerequisite skills must be an array");else for(const n of i.skills)typeof n!="string"?a.push("Prerequisite skill IDs must be strings"):ft.includes(n)||a.push(`Invalid prerequisite skill: "${n}". Must be one of: ${ft.join(", ")}`);if(i.spells!==void 0)if(!Array.isArray(i.spells))a.push("Prerequisite spells must be an array");else for(const n of i.spells)typeof n!="string"?a.push("Prerequisite spell names must be strings"):n.trim()===""&&a.push("Prerequisite spell name cannot be empty");return i.custom!==void 0&&typeof i.custom!="string"&&a.push("Prerequisite custom condition must be a string"),{valid:a.length===0,errors:a}}static validateClassFeatures(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateClassFeature(a);n.valid||t.push(`Feature at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of class features"]}}static validateRacialTraits(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateRacialTrait(a);n.valid||t.push(`Trait at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of racial traits"]}}static isAbility(e){return Pe(e)}static isValidFeatureId(e){return/^[a-z][a-z0-9_]*$/.test(e)}}function kr(s,e){return Fe.validateClassFeature(s,e)}function Tr(s,e){return Fe.validateRacialTrait(s,e)}function Cr(s){return Fe.validateClassFeatures(s)}function Ir(s){return Fe.validateRacialTraits(s)}function Er(s){const e=[];if(s==null)return{valid:!0,errors:[]};if(typeof s!="object"||Array.isArray(s))return{valid:!1,errors:["Prerequisites must be an object"]};const t=s;if(t.level!==void 0&&(typeof t.level!="number"?e.push("Prerequisite level must be a number"):t.level<1&&e.push(`Prerequisite level must be at least 1 (got: ${t.level})`)),t.casterLevel!==void 0&&(typeof t.casterLevel!="number"?e.push("Prerequisite casterLevel must be a number"):t.casterLevel<1&&e.push(`Prerequisite casterLevel must be at least 1 (got: ${t.casterLevel})`)),t.abilities!==void 0)if(typeof t.abilities!="object"||Array.isArray(t.abilities)||t.abilities===null)e.push("Prerequisite abilities must be a record");else for(const[a,i]of Object.entries(t.abilities))Pe(a)||e.push(`Invalid ability in prerequisites: "${a}". Must be one of: STR, DEX, CON, INT, WIS, CHA`),typeof i!="number"?e.push(`Ability score for ${a} must be a number (got: ${typeof i})`):(i<1||i>20)&&e.push(`Ability score for ${a} must be between 1 and 20 (got: ${i})`);if(t.class!==void 0&&typeof t.class!="string"&&e.push("Prerequisite class must be a string"),t.race!==void 0&&typeof t.race!="string"&&e.push("Prerequisite race must be a string"),t.subrace!==void 0&&(typeof t.subrace!="string"?e.push("Prerequisite subrace must be a string"):t.subrace.trim()===""&&e.push("Prerequisite subrace cannot be empty")),t.features!==void 0)if(!Array.isArray(t.features))e.push("Prerequisite features must be an array");else for(const a of t.features)typeof a!="string"&&e.push("Prerequisite feature IDs must be strings");if(t.skills!==void 0)if(!Array.isArray(t.skills))e.push("Prerequisite skills must be an array");else for(const a of t.skills)typeof a!="string"&&e.push("Prerequisite skill IDs must be strings");if(t.spells!==void 0)if(!Array.isArray(t.spells))e.push("Prerequisite spells must be an array");else for(const a of t.spells)typeof a!="string"?e.push("Prerequisite spell names must be strings"):a.trim()===""&&e.push("Prerequisite spell name cannot be empty");return t.custom!==void 0&&typeof t.custom!="string"&&e.push("Prerequisite custom condition must be a string"),{valid:e.length===0,errors:e}}function Ms(s,e){const t=[];if(!s)return{valid:!0,errors:[]};if(s.level!==void 0&&e.level<s.level&&t.push(`Requires level ${s.level} (current: ${e.level})`),s.casterLevel!==void 0&&e.level<s.casterLevel&&t.push(`Requires caster level ${s.casterLevel} (current: ${e.level})`),s.abilities)for(const[a,i]of Object.entries(s.abilities)){const n=e.ability_scores[a];(n===void 0||n<i)&&t.push(`Requires ${a} ${i}+ (current: ${n??0})`)}if(s.class!==void 0&&e.class!==s.class&&t.push(`Requires ${s.class} class (current: ${e.class})`),s.race!==void 0&&e.race!==s.race&&t.push(`Requires ${s.race} race (current: ${e.race})`),s.subrace!==void 0){const a=e.subrace||"";a!==s.subrace&&t.push(`Requires ${s.subrace} subrace (current: ${a||"none"})`)}if(s.skills&&s.skills.length>0)for(const a of s.skills){const i=e.skills[a];i!=="proficient"&&i!=="expertise"&&t.push(`Requires proficiency in ${a} (current: ${i??"none"})`)}if(s.features&&s.features.length>0){const a=e.class_features||[];for(const i of s.features)a.includes(i)||t.push(`Requires feature: ${i}`)}if(s.spells&&s.spells.length>0){const a=e.spells?.known_spells||[],i=e.spells?.cantrips||[],n=[...a,...i];for(const r of s.spells)n.includes(r)||t.push(`Requires spell: ${r}`)}return s.custom,{valid:t.length===0,errors:t}}const fi=["default","custom"],yi=["none","proficient","expertise"],vi=["class","background","feat","custom","racial","other"];class we{static validateSkill(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill must be an object"]};const a=e;if(!a.id||typeof a.id!="string"?t.push("Skill must have a valid id (string)"):this.isValidSkillId(a.id)||t.push(`Skill id must use lowercase_with_underscores format: "${a.id}"`),(!a.name||typeof a.name!="string")&&t.push("Skill must have a valid name (string)"),!a.ability||typeof a.ability!="string"?t.push("Skill must have an ability (string)"):lt.includes(a.ability)||t.push(`Invalid ability: "${a.ability}". Must be one of: ${lt.join(", ")}`),!a.source||typeof a.source!="string"?t.push("Skill must have a source (string)"):fi.includes(a.source)||t.push(`Invalid source: "${a.source}". Must be one of: ${fi.join(", ")}`),a.description!==void 0&&typeof a.description!="string"&&t.push("Skill description must be a string"),a.armorPenalty!==void 0&&typeof a.armorPenalty!="boolean"&&t.push("Skill armorPenalty must be a boolean"),a.customProperties!==void 0)if(typeof a.customProperties!="object"||Array.isArray(a.customProperties)||a.customProperties===null)t.push("Skill customProperties must be a record");else for(const[n,r]of Object.entries(a.customProperties)){const o=typeof r;if(!["string","number","boolean"].includes(o)&&!Array.isArray(r)&&t.push(`customProperties.${n} has invalid type: ${o}. Must be string, number, boolean, or string array`),Array.isArray(r))for(let l=0;l<r.length;l++)typeof r[l]!="string"&&t.push(`customProperties.${n}[${l}] must be a string`)}if(a.categories!==void 0)if(!Array.isArray(a.categories))t.push("Skill categories must be an array");else for(const n of a.categories)typeof n!="string"&&t.push(`Skill category must be a string (got: ${typeof n})`);if(a.tags!==void 0)if(!Array.isArray(a.tags))t.push("Skill tags must be an array");else for(const n of a.tags)typeof n!="string"&&t.push(`Skill tag must be a string (got: ${typeof n})`);a.lore!==void 0&&typeof a.lore!="string"&&t.push("Skill lore must be a string");const i=Be({icon:a.icon,image:a.image});return t.push(...i.map(n=>`Skill ${n}`)),{valid:t.length===0,errors:t}}static validateSkills(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSkill(a);n.valid||t.push(`Skill at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of skills"]}}static validateSkillProficiency(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill proficiency must be an object"]};const a=e;return!a.skillId||typeof a.skillId!="string"?t.push("Skill proficiency must have a valid skillId (string)"):this.isValidSkillId(a.skillId)||t.push(`Skill proficiency skillId must use lowercase_with_underscores format: "${a.skillId}"`),!a.level||typeof a.level!="string"?t.push("Skill proficiency must have a level (string)"):yi.includes(a.level)||t.push(`Invalid proficiency level: "${a.level}". Must be one of: ${yi.join(", ")}`),!a.source||typeof a.source!="string"?t.push("Skill proficiency must have a source (string)"):vi.includes(a.source)||t.push(`Invalid proficiency source: "${a.source}". Must be one of: ${vi.join(", ")}`),a.grantedBy!==void 0&&typeof a.grantedBy!="string"&&t.push("Skill proficiency grantedBy must be a string"),{valid:t.length===0,errors:t}}static validateSkillProficiencies(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSkillProficiency(a);n.valid||t.push(`Proficiency at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of skill proficiencies"]}}static validateSkillListDefinition(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill list definition must be an object"]};const a=e;if((!a.class||typeof a.class!="string")&&t.push("Skill list definition must have a class (string)"),typeof a.skillCount!="number"?t.push("Skill list definition must have a skillCount (number)"):(a.skillCount<0||!Number.isInteger(a.skillCount))&&t.push(`Skill list skillCount must be a non-negative integer (got: ${a.skillCount})`),!Array.isArray(a.availableSkills))t.push("Skill list definition must have availableSkills (array)");else{for(const i of a.availableSkills)typeof i!="string"?t.push(`availableSkills must contain only strings (got: ${typeof i})`):this.isValidSkillId(i)||t.push(`availableSkills contains invalid skill ID format: "${i}"`);typeof a.skillCount=="number"&&a.skillCount>a.availableSkills.length&&t.push(`skillCount (${a.skillCount}) cannot exceed availableSkills length (${a.availableSkills.length})`)}return a.hasExpertise!==void 0&&typeof a.hasExpertise!="boolean"&&t.push("Skill list hasExpertise must be a boolean"),a.expertiseCount!==void 0&&(typeof a.expertiseCount!="number"?t.push("Skill list expertiseCount must be a number"):a.expertiseCount<0||!Number.isInteger(a.expertiseCount)?t.push(`expertiseCount must be a non-negative integer (got: ${a.expertiseCount})`):Array.isArray(a.availableSkills)&&a.expertiseCount>a.availableSkills.length&&t.push(`expertiseCount (${a.expertiseCount}) cannot exceed availableSkills length (${a.availableSkills.length})`)),{valid:t.length===0,errors:t}}static isValidSkillId(e){return/^[a-z][a-z0-9_]*$/.test(e)}static isValidAbility(e){return Pe(e)}static validateSkillPrerequisites(e,t){return Ms(e,t)}}function Mr(s){return we.validateSkill(s)}function Dr(s){return we.validateSkills(s)}function Rr(s){return we.validateSkillProficiency(s)}function Pr(s){return we.validateSkillProficiencies(s)}function xr(s){return we.validateSkillListDefinition(s)}function Nr(s,e){return we.validateSkillPrerequisites(s,e)}const Yt=["Abjuration","Conjuration","Divination","Enchantment","Evocation","Illusion","Necromancy","Transmutation"],Zt=[0,1,2,3,4,5,6,7,8,9],bi=["V","S","M"];class De{static validateSpell(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Spell must be an object"]};const a=e;if((!a.name||typeof a.name!="string")&&t.push("Spell must have a valid name (string)"),typeof a.level!="number"?t.push("Spell must have a level (number)"):Zt.includes(a.level)||t.push(`Invalid spell level: "${a.level}". Must be one of: ${Zt.join(", ")}`),!a.school||typeof a.school!="string"?t.push("Spell must have a school (string)"):Yt.includes(a.school)||t.push(`Invalid school: "${a.school}". Must be one of: ${Yt.join(", ")}`),(!a.casting_time||typeof a.casting_time!="string")&&t.push("Spell must have a casting_time (string)"),(!a.range||typeof a.range!="string")&&t.push("Spell must have a range (string)"),!Array.isArray(a.components))t.push("Spell must have components (array)");else for(const n of a.components)typeof n!="string"?t.push(`Spell component must be a string (got: ${typeof n})`):bi.includes(n)||t.push(`Invalid spell component: "${n}". Must be one of: ${bi.join(", ")}`);(!a.duration||typeof a.duration!="string")&&t.push("Spell must have a duration (string)"),a.id!==void 0&&typeof a.id!="string"&&t.push("Spell id must be a string"),a.description!==void 0&&typeof a.description!="string"&&t.push("Spell description must be a string");const i=Be({icon:a.icon,image:a.image});if(t.push(...i.map(n=>`Spell ${n}`)),a.prerequisites!==void 0){const n=this.validatePrerequisites(a.prerequisites);n.valid||t.push(...n.errors.map(r=>`Prerequisites: ${r}`))}return{valid:t.length===0,errors:t}}static validateSpells(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSpell(a);n.valid||t.push(`Spell at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of spells"]}}static validatePrerequisites(e){return Er(e)}static validateSpellPrerequisites(e,t){return Ms(e,t)}static isValidAbility(e){return Pe(e)}static isValidSchool(e){return Yt.includes(e)}static isValidSpellLevel(e){return Zt.includes(e)}}function $r(s){return De.validateSpell(s)}function Lr(s){return De.validateSpells(s)}function Or(s){return De.validatePrerequisites(s)}function Br(s,e){return De.validateSpellPrerequisites(s,e)}class pe{constructor(){this.allSkillsCache=null,this.abilityCache=null,this.categoryCache=null,this.manager=R.getInstance()}static getInstance(){return pe.instance||(pe.instance=new pe),pe.instance}invalidateCache(){this.allSkillsCache=null,this.abilityCache=null,this.categoryCache=null}getSkill(e){return this.getAllSkills().find(a=>a.id===e)}getAllSkills(){if(!this.allSkillsCache){const e=this.manager.get("skills");this.allSkillsCache=e}return this.allSkillsCache}getSkillsByAbility(e){if(!this.abilityCache){this.abilityCache=new Map;const t=this.getAllSkills();for(const a of t)this.abilityCache.has(a.ability)||this.abilityCache.set(a.ability,[]),this.abilityCache.get(a.ability).push(a)}return this.abilityCache.get(e)||[]}getSkillsByCategory(e){if(!this.categoryCache){this.categoryCache=new Map;const t=this.getAllSkills();for(const a of t)if(a.categories&&a.categories.length>0)for(const i of a.categories)this.categoryCache.has(i)||this.categoryCache.set(i,[]),this.categoryCache.get(i).push(a)}return this.categoryCache.get(e)||[]}getCategories(){const e=this.getAllSkills(),t=new Set;for(const a of e)if(a.categories)for(const i of a.categories)t.add(i);return Array.from(t)}getSkillsBySource(e){return this.getAllSkills().filter(t=>t.source===e)}isValidSkill(e){return this.getSkill(e)!==void 0}validateSkill(e){return we.validateSkill(e)}validatePrerequisites(e,t){return we.validateSkillPrerequisites(e.prerequisites,t)}getQueryStats(){const e=this.getAllSkills(),t=e.filter(r=>r.source==="default"),a=e.filter(r=>r.source==="custom"),i={STR:0,DEX:0,CON:0,INT:0,WIS:0,CHA:0};for(const r of e)i[r.ability]++;const n=this.getCategories();return{totalSkills:e.length,defaultSkills:t.length,customSkills:a.length,skillsByAbility:i,categories:n}}getSkillCount(){return this.getAllSkills().length}getAvailableSkills(e){return this.getAllSkills().filter(t=>t.prerequisites?this.validatePrerequisites(t,e).valid:!0)}}function Fr(){return pe.getInstance()}const it=["STR","DEX","CON","INT","WIS","CHA"],wi=["weapon","armor","item","box"],Si=["common","uncommon","rare","very_rare","legendary"],_i=["stat_bonus","skill_proficiency","ability_unlock","passive_modifier","special_property","damage_bonus","stat_requirement"],Ai=["vs_creature_type","at_time_of_day","wielder_race","wielder_class","while_equipped","on_hit","on_damage_taken","custom"],ki=["day","night","dawn","dusk"],Ti=["proficient","expertise"],Ci=["default","custom"],yt=/^\d+d\d+([+-]\d+)?$/,Hr=/^range_\d+_\d+$/;class Dt{static validateEquipment(e){const t=[];if((!e.name||typeof e.name!="string")&&t.push("Equipment must have a valid name (required)"),(!e.type||!wi.includes(e.type))&&t.push(`Equipment type must be one of: ${wi.join(", ")}`),(!e.rarity||!Si.includes(e.rarity))&&t.push(`Equipment rarity must be one of: ${Si.join(", ")}`),(typeof e.weight!="number"||e.weight<0)&&t.push("Equipment weight must be a non-negative number"),e.source!==void 0&&!Ci.includes(e.source)&&t.push(`Equipment source must be one of: ${Ci.join(", ")}`),e.properties)if(!Array.isArray(e.properties))t.push("Equipment properties must be an array");else for(let n=0;n<e.properties.length;n++){const r=this.validateProperty(e.properties[n]);r.valid||t.push(`Property at index ${n}: ${r.errors?.join(", ")}`)}if(e.grantsFeatures)if(!Array.isArray(e.grantsFeatures))t.push("grantsFeatures must be an array");else for(let n=0;n<e.grantsFeatures.length;n++){const r=e.grantsFeatures[n],o=this.validateFeatureReference(r,n);o.valid||t.push(...o.errors||[])}if(e.grantsSkills)if(!Array.isArray(e.grantsSkills))t.push("grantsSkills must be an array");else for(let n=0;n<e.grantsSkills.length;n++){const r=e.grantsSkills[n],o=this.validateSkillReference(r.skillId,n);o.valid||t.push(...o.errors||[]),r.level&&!Ti.includes(r.level)&&t.push(`grantsSkills[${n}]: proficiency level must be 'proficient' or 'expertise'`)}if(e.grantsSpells)if(!Array.isArray(e.grantsSpells))t.push("grantsSpells must be an array");else for(let n=0;n<e.grantsSpells.length;n++){const r=e.grantsSpells[n];(!r.spellId||typeof r.spellId!="string")&&t.push(`grantsSpells[${n}]: must have a valid spellId`),r.uses!==void 0&&(typeof r.uses!="number"||r.uses<0)&&t.push(`grantsSpells[${n}]: uses must be a non-negative number`),r.level!==void 0&&(typeof r.level!="number"||r.level<0)&&t.push(`grantsSpells[${n}]: level must be a non-negative number`)}if(e.damage){const n=this.validateDamageInfo(e.damage);n.valid||t.push(...n.errors||[])}const a=e.acBonus??e.armor_class;if(a!==void 0&&typeof a!="number"&&t.push("AC bonus must be a number"),e.weaponProperties&&(Array.isArray(e.weaponProperties)||t.push("weaponProperties must be an array")),e.spawnWeight!==void 0){const n=this.validateSpawnWeight(e.spawnWeight);n.valid||t.push(...n.errors||[])}if(e.templateId!==void 0&&typeof e.templateId!="string"&&t.push("templateId must be a string"),e.tags&&(Array.isArray(e.tags)||t.push("tags must be an array")),e.type==="box"&&!e.boxContents&&t.push('Equipment of type "box" must have a boxContents property'),e.boxContents!==void 0){const n=this.validateBoxContents(e.boxContents);n.valid||t.push(...n.errors||[])}const i=Be({icon:e.icon,image:e.image});return t.push(...i.map(n=>`Equipment ${n}`)),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateProperty(e){const t=[];if((!e.type||!_i.includes(e.type))&&t.push(`Property type must be one of: ${_i.join(", ")}`),(!e.target||typeof e.target!="string")&&t.push("Property must have a valid target"),e.value===void 0)t.push("Property must have a value");else{const a=this.validatePropertyValue(e.type,e.target,e.value);a.valid||t.push(...a.errors||[])}if(e.condition){const a=this.validateCondition(e.condition);a.valid||t.push(...a.errors||[])}return e.stackable!==void 0&&typeof e.stackable!="boolean"&&t.push("stackable must be a boolean"),e.description!==void 0&&typeof e.description!="string"&&t.push("description must be a string"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validatePropertyValue(e,t,a){const i=[];switch(e){case"stat_bonus":it.includes(t)||i.push(`stat_bonus target must be a valid ability: ${it.join(", ")}`),typeof a!="number"&&i.push("stat_bonus value must be a number");break;case"skill_proficiency":typeof t!="string"?i.push("skill_proficiency target must be a string"):pe.getInstance().isValidSkill(t)||i.push(`skill_proficiency target must be a valid skill ID: "${t}" not found in SkillQuery`),a!=="proficient"&&a!=="expertise"&&typeof a!="boolean"&&i.push('skill_proficiency value must be "proficient", "expertise", or a boolean');break;case"ability_unlock":(typeof t!="string"||t.length===0)&&i.push("ability_unlock must have a valid target"),typeof a!="boolean"&&typeof a!="number"&&i.push("ability_unlock value must be a boolean or number");break;case"passive_modifier":!["ac","armor_class","speed","max_hp","hp_max","initiative","saving_throws","attack_roll","damage_roll"].includes(t)&&!it.includes(t)&&pe.getInstance().isValidSkill(t),typeof a!="number"&&typeof a!="boolean"&&i.push("passive_modifier value must be a number or boolean");break;case"stat_requirement":it.includes(t)||i.push(`stat_requirement target must be a valid ability: ${it.join(", ")}`),typeof a!="number"&&i.push("stat_requirement value must be a number (minimum score)");break;case"special_property":(typeof t!="string"||t.length===0)&&i.push("special_property must have a valid target");break;case"damage_bonus":(typeof t!="string"||t.length===0)&&i.push("damage_bonus must have a valid target (damage type)"),typeof a=="string"&&!yt.test(a)?i.push('damage_bonus value must be a dice format (e.g., "1d6") or a number'):typeof a!="number"&&typeof a!="string"&&i.push("damage_bonus value must be a number or dice string");break;default:i.push(`Unknown property type: ${e}`)}return{valid:i.length===0,errors:i.length>0?i:void 0}}static validateCondition(e){const t=[];(!e.type||!Ai.includes(e.type))&&t.push(`Condition type must be one of: ${Ai.join(", ")}`);const a=e.type,i=e.value;switch(a){case"at_time_of_day":ki.includes(e.value)||t.push(`at_time_of_day value must be one of: ${ki.join(", ")}`);break;case"wielder_race":case"wielder_class":case"vs_creature_type":(typeof i!="string"||i.length===0)&&t.push(`${a} condition must have a valid string value`);break;case"while_equipped":case"on_hit":case"on_damage_taken":typeof i!="boolean"&&t.push(`${a} condition must have a boolean value`);break;case"custom":(typeof i!="string"||i.length===0)&&t.push("custom condition must have a valid value string"),(!e.description||typeof e.description!="string")&&t.push("custom condition must have a description");break;default:t.push(`Unknown condition type: ${a}`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}static validateFeatureReference(e,t){const a=[];if(typeof e=="string"){const i=re.getInstance(),n=i.getClassFeatureById(e),r=n?void 0:i.getRacialTraitById(e);!n&&!r&&a.push(`grantsFeatures[${t}]: Feature "${e}" not found in FeatureQuery`)}else if(typeof e=="object"&&e!==null){if((!e.id||typeof e.id!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid id`),(!e.name||typeof e.name!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid name`),(!e.description||typeof e.description!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid description`),!e.effects||!Array.isArray(e.effects))a.push(`grantsFeatures[${t}]: Mini-feature must have an effects array`);else for(let i=0;i<e.effects.length;i++){const n=this.validateProperty(e.effects[i]);n.valid||a.push(`grantsFeatures[${t}].effects[${i}]: ${n.errors?.join(", ")}`)}e.source!=="equipment_inline"&&a.push(`grantsFeatures[${t}]: Mini-feature must have source: 'equipment_inline'`)}else a.push(`grantsFeatures[${t}]: Feature reference must be a string or object`);return{valid:a.length===0,errors:a.length>0?a:void 0}}static validateEquipmentFeatureReference(e){const t=re.getInstance(),a=t.getClassFeatureById(e),i=a?void 0:t.getRacialTraitById(e);return!!(a||i)}static validateSkillReference(e,t){const a=[],i=t!==void 0?`grantsSkills[${t}]`:"skill";return!e||typeof e!="string"?(a.push(`${i}: must have a valid skillId`),{valid:!1,errors:a}):(pe.getInstance().isValidSkill(e)||a.push(`${i}: Skill "${e}" not found in SkillQuery`),{valid:a.length===0,errors:a.length>0?a:void 0})}static validateEquipmentSkillReference(e){return pe.getInstance().isValidSkill(e)}static validateDamageInfo(e){const t=[];if(!e)return{valid:!0};if(typeof e=="string"){const a=e.trim().split(/\s+/);if(a.length<2)t.push(`Damage string must be in format "NdM type" (e.g., "1d8 slashing"), got: "${e}"`);else{const i=a[0];yt.test(i)||t.push(`Damage dice must be in format "NdM" (e.g., "1d8"), got: ${i}`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}return!e.dice||typeof e.dice!="string"?t.push('Damage must have a valid dice string (e.g., "1d8")'):yt.test(e.dice)||t.push(`Damage dice must be in format "NdM" (e.g., "1d8", "2d6"), got: ${e.dice}`),(!e.damageType||typeof e.damageType!="string")&&t.push("Damage must have a valid damageType"),e.versatile!==void 0&&(typeof e.versatile!="string"?t.push("Damage versatile must be a string (dice format)"):yt.test(e.versatile)||t.push(`Damage versatile must be in dice format "NdM" (e.g., "1d10"), got: ${e.versatile}`)),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateSpawnWeight(e){const t=[];return typeof e!="number"?t.push("Spawn weight must be a number"):e<0?t.push("Spawn weight must be non-negative (0 = never random, still usable by game logic)"):Number.isFinite(e)||t.push("Spawn weight must be a finite number"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateBoxContents(e){const t=[];if(!e||typeof e!="object")return t.push("boxContents must be an object"),{valid:!1,errors:t};if(!Array.isArray(e.drops))return t.push("boxContents.drops must be an array"),{valid:!1,errors:t};if(e.consumeOnOpen!==void 0&&typeof e.consumeOnOpen!="boolean"&&t.push("boxContents.consumeOnOpen must be a boolean"),e.openRequirements!==void 0)if(!Array.isArray(e.openRequirements))t.push("boxContents.openRequirements must be an array");else for(let a=0;a<e.openRequirements.length;a++){const i=this.validateBoxOpenRequirement(e.openRequirements[a]);i.valid||t.push(...(i.errors||[]).map(n=>`openRequirements[${a}]: ${n}`))}for(let a=0;a<e.drops.length;a++){const i=e.drops[a];if(!i||!Array.isArray(i.pool)){t.push(`boxContents.drops[${a}]: pool must be an array`);continue}if(i.pool.length===0){t.push(`boxContents.drops[${a}]: pool must not be empty`);continue}let n=0;for(let r=0;r<i.pool.length;r++){const o=i.pool[r],l=`boxContents.drops[${a}].pool[${r}]`;typeof o.weight!="number"||o.weight<0?t.push(`${l}: weight must be a non-negative number`):n+=o.weight,o.itemName!==void 0&&o.gold!==void 0&&t.push(`${l}: itemName and gold are mutually exclusive`),o.itemName===void 0&&o.gold===void 0&&t.push(`${l}: must have either itemName or gold`),o.itemName!==void 0&&(typeof o.itemName!="string"||o.itemName.length===0?t.push(`${l}: itemName must be a non-empty string`):R.getInstance().get("equipment").some(h=>h.name===o.itemName)||t.push(`${l}: itemName "${o.itemName}" not found in equipment registry`)),o.gold!==void 0&&(typeof o.gold!="number"||o.gold<0)&&t.push(`${l}: gold must be a non-negative number`),o.quantity!==void 0&&(typeof o.quantity!="number"||o.quantity<1||!Number.isInteger(o.quantity))&&t.push(`${l}: quantity must be a positive integer`)}i.pool.length>0&&Math.abs(n-100)>.001&&t.push(`boxContents.drops[${a}]: pool weights sum to ${n}, expected 100`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}static validateBoxOpenRequirement(e){const t=[];return!e||typeof e!="object"?(t.push("BoxOpenRequirement must be an object"),{valid:!1,errors:t}):(!e.itemName||typeof e.itemName!="string"||e.itemName.length===0?t.push("BoxOpenRequirement.itemName must be a non-empty string"):R.getInstance().get("equipment").some(r=>r.name===e.itemName)||t.push(`BoxOpenRequirement.itemName "${e.itemName}" not found in equipment registry`),e.quantity!==void 0&&(typeof e.quantity!="number"||e.quantity<1||!Number.isInteger(e.quantity))&&t.push("BoxOpenRequirement.quantity must be a positive integer"),{valid:t.length===0,errors:t.length>0?t:void 0})}static validateModification(e){const t=[];if((!e.id||typeof e.id!="string")&&t.push("Modification must have a valid id"),(!e.name||typeof e.name!="string")&&t.push("Modification must have a valid name"),(!e.appliedAt||typeof e.appliedAt!="string")&&t.push("Modification must have a valid appliedAt timestamp"),(!e.source||typeof e.source!="string")&&t.push("Modification must have a valid source"),!e.properties||!Array.isArray(e.properties))t.push("Modification must have a properties array");else for(let a=0;a<e.properties.length;a++){const i=this.validateProperty(e.properties[a]);i.valid||t.push(`Modification properties[${a}]: ${i.errors?.join(", ")}`)}if(e.addsFeatures)if(!Array.isArray(e.addsFeatures))t.push("Modification addsFeatures must be an array");else for(let a=0;a<e.addsFeatures.length;a++){const i=this.validateFeatureReference(e.addsFeatures[a],a);i.valid||t.push(`Modification addsFeatures[${a}]: ${i.errors?.join(", ")}`)}if(e.addsSkills)if(!Array.isArray(e.addsSkills))t.push("Modification addsSkills must be an array");else for(let a=0;a<e.addsSkills.length;a++){const i=e.addsSkills[a],n=this.validateSkillReference(i.skillId,a);n.valid||t.push(...(n.errors||[]).map(r=>`Modification ${r}`)),i.level&&!Ti.includes(i.level)&&t.push(`Modification addsSkills[${a}]: proficiency level must be 'proficient' or 'expertise'`)}return e.addsSpells&&(Array.isArray(e.addsSpells)||t.push("Modification addsSpells must be an array")),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateMiniFeature(e){const t=[];if((!e.id||typeof e.id!="string")&&t.push("Mini-feature must have a valid id"),(!e.name||typeof e.name!="string")&&t.push("Mini-feature must have a valid name"),(!e.description||typeof e.description!="string")&&t.push("Mini-feature must have a valid description"),!e.effects||!Array.isArray(e.effects))t.push("Mini-feature must have an effects array");else for(let a=0;a<e.effects.length;a++){const i=this.validateProperty(e.effects[a]);i.valid||t.push(`Mini-feature effects[${a}]: ${i.errors?.join(", ")}`)}return e.source!=="equipment_inline"&&t.push('Mini-feature must have source: "equipment_inline"'),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateACBonus(e){const t=[];return typeof e!="number"?t.push("AC bonus must be a number"):e<0?t.push("AC bonus cannot be negative"):Number.isFinite(e)||t.push("AC bonus must be a finite number"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateWeaponProperties(e){const t=[];if(!Array.isArray(e))return t.push("Weapon properties must be an array"),{valid:!1,errors:t};for(let a=0;a<e.length;a++){const i=e[a];if(typeof i!="string"){t.push(`Weapon property at index ${a} must be a string`);continue}i.startsWith("range_")&&(Hr.test(i)||t.push(`Weapon property "${i}" at index ${a} must be in format "range_MIN_MAX" (e.g., "range_20_60")`))}return{valid:t.length===0,errors:t.length>0?t:void 0}}}const Ce={Barbarian:{primary_ability:"STR",hit_die:12,saving_throws:["STR","CON"],is_spellcaster:!1,skill_count:2,available_skills:["athletics","animal_handling","intimidation","nature","perception","survival"],has_expertise:!1,description:"A fierce warrior of primitive background who can enter a battle rage. Barbarians draw on primal fury to unleash devastating attacks, shrug off wounds that would fell others, and channel their totem spirits' power. They thrive on the front lines, where their rage makes them unstoppable forces of nature."},Bard:{primary_ability:"CHA",hit_die:8,saving_throws:["DEX","CHA"],is_spellcaster:!0,skill_count:3,available_skills:["athletics","acrobatics","sleight_of_hand","stealth","arcana","history","investigation","nature","religion","animal_handling","insight","medicine","perception","survival","deception","intimidation","performance","persuasion"],has_expertise:!0,expertise_count:2,description:"An inspiring magician whose power echoes the music of creation. Bards weave magic through music, poetry, and performance, inspiring allies, demoralizing enemies, and wielding versatile spells. They are jack-of-all-trades who can fill any role, from frontline support to devastating spellcaster."},Cleric:{primary_ability:"WIS",hit_die:8,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["history","insight","medicine","persuasion","religion"],has_expertise:!1,description:"A priestly champion who wields divine magic in service of a higher power. Clerics serve as intermediaries between the mortal world and the divine, wielding healing and destructive magic granted by their deities. They can wear heavy armor, wield powerful weapons, and specialize in different domains of divine influence."},Druid:{primary_ability:"WIS",hit_die:8,saving_throws:["INT","WIS"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","animal_handling","insight","medicine","nature","perception","religion","survival"],has_expertise:!1,description:"A priest of the Old Faith, wielding the powers of nature and adopting animal forms. Druids revere nature and gain their magic from the natural world, commanding the elements, transforming into beasts, and calling upon the forces of life and growth. They serve as guardians of the wild places and mediators between civilization and nature."},Fighter:{primary_ability:"STR",hit_die:10,saving_throws:["STR","CON"],is_spellcaster:!1,skill_count:2,available_skills:["acrobatics","animal_handling","athletics","history","insight","intimidation","perception","survival"],has_expertise:!1,description:"A master of martial combat, skilled with a variety of weapons and armor. Fighters learn countless fighting techniques, specializing in certain styles of combat. They are the backbone of any adventuring party, capable of withstanding tremendous punishment while dishing out devastating damage through martial prowess alone."},Monk:{primary_ability:"DEX",hit_die:8,saving_throws:["STR","DEX"],is_spellcaster:!1,skill_count:2,available_skills:["acrobatics","athletics","history","insight","religion","stealth"],has_expertise:!1,description:"A master of martial arts, harnessing the power of the body in pursuit of physical and spiritual perfection. Monks use their bodies as weapons, channeling ki energy to perform extraordinary feats. They are agile warriors who can strike with deadly precision, evade attacks with supernatural grace, and harness mystical abilities."},Paladin:{primary_ability:"STR",hit_die:10,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["athletics","insight","intimidation","medicine","persuasion","religion"],has_expertise:!1,description:"A holy warrior bound to a sacred oath. Paladins combine martial prowess with divine magic, smiting foes with holy fire and healing allies with sacred power. They are paragons of their beliefs, radiating auras that protect allies and punish enemies. Their strength comes from their unwavering devotion to their oath."},Ranger:{primary_ability:"DEX",hit_die:10,saving_throws:["STR","DEX"],is_spellcaster:!0,skill_count:3,available_skills:["animal_handling","athletics","insight","investigation","nature","perception","stealth","survival"],has_expertise:!1,description:"A warrior who combats threats on the edges of civilization. Rangers are hunters and trackers who know the wilderness intimately, moving swiftly through natural terrain and striking from a distance. They develop supernatural connections to their chosen enemies and environments, gaining deadly precision against favored foes."},Rogue:{primary_ability:"DEX",hit_die:8,saving_throws:["DEX","INT"],is_spellcaster:!1,skill_count:4,available_skills:["acrobatics","athletics","deception","insight","intimidation","investigation","perception","performance","persuasion","sleight_of_hand","stealth"],has_expertise:!0,expertise_count:2,description:"A scoundrel who uses stealth and trickery to overcome obstacles and enemies. Rogues excel at skills, finding traps, and striking devastating blows from hiding. Whether as thieves, assassins, or spies, they are versatile problem-solvers who can disable threats before allies even know they exist."},Sorcerer:{primary_ability:"CHA",hit_die:6,saving_throws:["CON","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","deception","insight","intimidation","persuasion","religion"],has_expertise:!1,description:"A spellcaster who draws on inherent magic from a supernatural bloodline or cosmic source. Sorcerers have no need for spellbooks—their power comes from within, born of draconic heritage, wild magic, or other supernatural origins. They can manipulate and metamagic their spells in ways other casters cannot."},Warlock:{primary_ability:"CHA",hit_die:8,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","deception","history","intimidation","investigation","nature","religion"],has_expertise:!1,description:"A wielder of magic that is derived from a bargain with an extraplanar entity. Warlocks gain their powers through pacts with powerful beings—fiends, fey, Great Old Ones, or other eldritch entities. In exchange for service, they receive mystical abilities and can cast eldritch invocations that blur the line between spell and supernatural ability."},Wizard:{primary_ability:"INT",hit_die:6,saving_throws:["INT","WIS"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","history","insight","investigation","medicine","religion"],has_expertise:!1,description:"A scholarly magic-user capable of manipulating the structures of reality. Wizards are masters of arcane magic, studying spellbooks to unlock the secrets of the universe. They specialize in specific schools of magic, from evocation to illusion, and have access to the widest variety of spells of any class."}},Ds=["Barbarian","Bard","Cleric","Druid","Fighter","Monk","Paladin","Ranger","Rogue","Sorcerer","Warlock","Wizard"],Rs={Barbarian:{primary:"bass",secondary:"amplitude",bass:1,amplitude:.7},Fighter:{primary:"bass",secondary:"amplitude",bass:.9,amplitude:.8},Paladin:{primary:"bass",secondary:"mid",bass:.8,mid:.5},Rogue:{primary:"treble",treble:1},Ranger:{primary:"treble",secondary:"bass",treble:.8,bass:.5},Monk:{primary:"treble",secondary:"mid",treble:.7,mid:.6},Wizard:{primary:"mid",mid:1},Cleric:{primary:"mid",secondary:"amplitude",mid:.8,amplitude:.6},Druid:{primary:"mid",secondary:"bass",mid:.7,bass:.6},Bard:{primary:"amplitude",secondary:"mid",tertiary:"treble",amplitude:.8,mid:.6,treble:.3},Sorcerer:{primary:"amplitude",secondary:"chaos",amplitude:.9},Warlock:{primary:"amplitude",secondary:"treble",amplitude:.7,treble:.5}},Ia={"Acid Splash":{name:"Acid Splash",level:0,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"Hurls a bubble of acid at a creature or point. On hit, deals acid damage to creatures in a small splash radius. Dexterity save for half damage."},"Fire Bolt":{name:"Fire Bolt",level:0,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Hurls a mote of fire at a creature or object. On hit, deals fire damage. One of the most reliable damaging cantrips for ranged combat."},Light:{name:"Light",level:0,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","M"],duration:"1 hour",description:"Touch one object to make it shed bright light in a 20-foot radius and dim light for another 20 feet. Completely illuminates dark areas and can be moved with the touched object."},"Mage Hand":{name:"Mage Hand",level:0,school:"Conjuration",casting_time:"1 action",range:"30 feet",components:["V","S"],duration:"Concentration, up to 1 minute",description:"Conjures a spectral, floating hand at a point you choose. The hand can manipulate objects, open unlocked doors, or stow items. It cannot attack or activate magical items."},Mending:{name:"Mending",level:0,school:"Transmutation",casting_time:"1 minute",range:"Touch",components:["V","S","M"],duration:"Instantaneous",description:"Repairs a single break or tear in an object you touch, such as a broken chain link, two halves of a broken key, or a torn cloak. Cannot restore magic items or creatures."},Message:{name:"Message",level:0,school:"Transmutation",casting_time:"1 action",range:"120 feet",components:["V","S","M"],duration:"1 round",description:"Points your finger toward a creature within range and whisper a message. The target hears the message and can reply in a whisper that only you can hear. Perfect for covert communication."},Prestidigitation:{name:"Prestidigitation",level:0,school:"Transmutation",casting_time:"1 action",range:"10 feet",components:["V","S"],duration:"Up to 1 hour",description:"Performs minor magical tricks for your amusement. Can chill, warm, flavor, clean, or soil small objects. Create harmless sensory effects like colored lights or faint sounds."},"Sacred Flame":{name:"Sacred Flame",level:0,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"Calls down flame-like radiance on a target. The target must succeed on a Dexterity save or take radiant damage. This spell can be cast from behind cover and ignores half and three-quarters cover."},"Shocking Grasp":{name:"Shocking Grasp",level:0,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Instantaneous",description:"Lightning springs from your hand to deliver a shock to a creature. You have advantage on the attack roll if the target is wearing metal armor. The shock cannot be used to make reactions of opportunity."},"Vicious Mockery":{name:"Vicious Mockery",level:0,school:"Enchantment",casting_time:"1 action",range:"60 feet",components:["V"],duration:"Instantaneous",description:"Utters a cruel insult to a creature within range. The target must make a Wisdom save or take psychic damage. A creative and subtle way to attack enemies in social situations."},"Burning Hands":{name:"Burning Hands",level:1,school:"Evocation",casting_time:"1 action",range:"Self (15-foot cone)",components:["V","S"],duration:"Instantaneous",description:"Ignites a cone of fire from your outstretched hands. Each creature in the area must make a Dexterity save or take fire damage. Devastating damage at close range but dangerous to allies in the cone."},"Charm Person":{name:"Charm Person",level:1,school:"Enchantment",casting_time:"1 action",range:"30 feet",components:["V","S"],duration:"1 hour",description:"Attempts to charm a humanoid you can see. The target must make a Wisdom save or be charmed by you. While charmed, it regards you as a friendly acquaintance. The effect ends if you or your allies do anything harmful to it."},"Cure Wounds":{name:"Cure Wounds",level:1,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Instantaneous",description:"A creature you touch regains hit points equal to 1d8 plus your spellcasting ability modifier. The amount increases by 1d8 for each slot level above 1st. This spell has no effect on undead or constructs."},"Detect Magic":{name:"Detect Magic",level:1,school:"Divination",casting_time:"1 action",range:"Self",components:["V","S"],duration:"Concentration, up to 10 minutes",description:"For the duration, you sense the presence of magic within 30 feet of you. You can identify the school of magic and locate the source, but not learn its exact properties. Essential for identifying magical auras."},"Disguise Self":{name:"Disguise Self",level:1,school:"Illusion",casting_time:"1 action",range:"Self",components:["V","S"],duration:"1 hour",description:"Makes you—including your clothing, armor, weapons, and other belongings—look different until the spell ends. You can change your height, weight, facial features, voice, and even appear as another creature of the same size."},"Expeditious Retreat":{name:"Expeditious Retreat",level:1,school:"Transmutation",casting_time:"1 bonus action",range:"Self",components:["V","S"],duration:"Concentration, up to 10 minutes",description:"Doubles your walking speed and grants the ability to take the Disengage or Hide action as a bonus action on each turn. Essential for escape or closing the distance safely."},"False Life":{name:"False Life",level:1,school:"Necromancy",casting_time:"1 action",range:"Self",components:["V","S","M"],duration:"1 hour",description:"Bolsters your vitality with necromantic magic, gaining temporary hit points equal to 1d4 plus your spellcasting ability modifier. The temporary hit points increase by 1d4 for each slot level above 1st."},"Feather Fall":{name:"Feather Fall",level:1,school:"Transmutation",casting_time:"1 reaction",range:"60 feet",components:["V","M"],duration:"1 minute",description:"Choose up to five falling creatures within range. A falling creature's rate of descent slows to 60 feet per round until the spell ends. If the creature lands before the spell ends, it takes no falling damage and lands on its feet."},Grease:{name:"Grease",level:1,school:"Conjuration",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"1 minute",description:"Covers a 10-foot square on the ground in slippery grease. Creatures in the area must succeed on a Dexterity save or fall prone. Creatures entering the area or ending their turn there must also save or fall."},"Healing Word":{name:"Healing Word",level:1,school:"Evocation",casting_time:"1 bonus action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"A creature of your choice that you can see within range regains hit points equal to 1d4 plus your spellcasting ability modifier. This spell has no effect on undead or constructs. Essential for saving fallen allies from a distance."},Identify:{name:"Identify",level:1,school:"Divination",casting_time:"1 minute",range:"Touch",components:["V","S","M"],duration:"Instantaneous",description:"Choose one object that you must touch throughout the casting. Learn if it's magically cursed or has been affected by a spell. You learn the properties and how to use them if it's a magic item. Requires a 100gp pearl focus."},"Mage Armor":{name:"Mage Armor",level:1,school:"Abjuration",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"8 hours",description:"Touch a willing creature to protect it with a magical force. The target's AC becomes 13 + its Dexterity modifier, minus any magical penalties. The spell ends if the target dons armor or if you dismiss it."},"Magic Missile":{name:"Magic Missile",level:1,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Creates three glowing darts of magical force. Each dart hits a creature of your choice that you can see within range. A dart deals 1d4 + 1 force damage. For each slot level above 1st, the spell creates one more dart."},Shield:{name:"Shield",level:1,school:"Abjuration",casting_time:"1 reaction",range:"Self",components:["V","S"],duration:"1 round",description:"An invisible barrier of magical force appears and protects you. Until the start of your next turn, you have a +5 bonus to AC, including against the triggering attack, and you take no damage from magic missile."},Sleep:{name:"Sleep",level:1,school:"Enchantment",casting_time:"1 action",range:"90 feet",components:["V","S","M"],duration:"1 minute",description:"Rolls 5d8; the total is how many hit points of creatures this spell can affect. Creatures in a 20-foot cube within range are affected in ascending order of current hit points. Unconscious creatures fall prone and are incapacitated."},Thunderwave:{name:"Thunderwave",level:1,school:"Evocation",casting_time:"1 action",range:"Self (15-foot cube)",components:["V","S"],duration:"Instantaneous",description:"A wave of thunderous force sweeps out from you. Each creature in the area must make a Constitution save or take thunder damage and be pushed 10 feet away. Unattended objects are also pushed and take damage."},"Acid Arrow":{name:"Acid Arrow",level:2,school:"Evocation",casting_time:"1 action",range:"90 feet",components:["V","S","M"],duration:"Instantaneous",description:"Hurls a bubble of acid at a point you choose within range. On a hit, the target takes acid damage immediately and additional acid damage at the end of its next turn. This secondary damage ignores resistance but not immunity."},"Aganazzar's Scorcher":{name:"Aganazzar's Scorcher",level:2,school:"Evocation",casting_time:"1 action",range:"30 feet",components:["V","S","M"],duration:"Instantaneous",description:"A line of roaring flame erupts from you in a 30-foot line. Each creature in the line must make a Dexterity save or take fire damage. A flamethrower-like effect that excels at controlling narrow corridors."},Blur:{name:"Blur",level:2,school:"Illusion",casting_time:"1 action",range:"Self",components:["V"],duration:"Concentration, up to 1 minute",description:"Your body becomes blurred, shifting and wavering to all who can see you. Until the spell ends, attacks against you have disadvantage, and you have advantage on Dexterity saving throws. Essential defense for spellcasters."},"Detect Thoughts":{name:"Detect Thoughts",level:2,school:"Divination",casting_time:"1 action",range:"Self",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"For the duration, you can read the thoughts of certain creatures. You initially sense the presence of thinking creatures, then can focus on one to read its surface thoughts. Deeper thoughts require a contest of Intelligence."},Fireball:{name:"Fireball",level:3,school:"Evocation",casting_time:"1 action",range:"150 feet",components:["V","S","M"],duration:"Instantaneous",description:"A bright streak flashes from your pointing finger to a point you choose within range and then blossoms with a low roar into an explosion of flame. Each creature in a 20-foot-radius sphere must make a Dexterity save or take fire damage."},"Hold Person":{name:"Hold Person",level:2,school:"Enchantment",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"Choose a humanoid that you can see within range. The target must succeed on a Wisdom save or be paralyzed. The spell can target additional humanoids at higher slot levels. Paralyzed creatures are automatically critical hits."},Invisibility:{name:"Invisibility",level:2,school:"Illusion",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"A creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person. The spell ends if the target attacks or casts a spell."},Knock:{name:"Knock",level:2,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V"],duration:"Instantaneous",description:"Choose an object that you can see within range. The object can be a door, a box, a chest, a set of manacles, a padlock, or another object that contains a mundane or magical means of preventing access."},"Misty Step":{name:"Misty Step",level:2,school:"Conjuration",casting_time:"1 bonus action",range:"Self",components:["V"],duration:"Instantaneous",description:"Briefly surrounded by silvery mist, you teleport up to 30 feet to an unoccupied space you can see. You can bring along objects as long as their weight doesn't exceed what you can carry. A quick escape or repositioning tool."},"Mirror Image":{name:"Mirror Image",level:2,school:"Illusion",casting_time:"1 action",range:"Self",components:["V","S"],duration:"Concentration, up to 1 minute",description:"Three illusory duplicates of yourself appear in your space. Until the spell ends, the duplicates move with you and mimic your actions, shifting position so it's impossible to track which image is real."},"Scorching Ray":{name:"Scorching Ray",level:2,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"You create three rays of fire and hurl them at targets within range. You can aim them at one target or several. Make a ranged spell attack for each ray. On a hit, the target takes fire damage."},Shatter:{name:"Shatter",level:2,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Instantaneous",description:"A sudden loud ringing noise, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution save or take thunder damage."},Suggestion:{name:"Suggestion",level:2,school:"Enchantment",casting_time:"1 action",range:"30 feet",components:["V","M"],duration:"Concentration, up to 1 minute",description:"Suggest a course of activity to a creature you can see within range and can understand. The target must make a Wisdom save or pursue the course of action. The suggestion must be worded so as to sound reasonable."},"Animate Dead":{name:"Animate Dead",level:3,school:"Necromancy",casting_time:"1 minute",range:"10 feet",components:["V","S","M"],duration:"Instantaneous",description:"This spell creates an undead servant. Choose a pile of bones or a corpse of a Medium or Small humanoid within range. Your spell animates it as a skeleton or a zombie that obeys your commands until destroyed."},Blink:{name:"Blink",level:3,school:"Transmutation",casting_time:"1 action",range:"Self",components:["V","S"],duration:"1 minute",description:"Roll a d20 at the start of each of your turns. On an 11 or higher, you vanish from your current plane of existence and appear in the Ethereal Plane for the round, returning to where you were. Attacks against you have disadvantage while blinking."},Counterspell:{name:"Counterspell",level:3,school:"Abjuration",casting_time:"1 reaction",range:"60 feet",components:["S"],duration:"Instantaneous",description:"You attempt to interrupt a creature in the process of casting a spell. The creature must make a Constitution save. If the spell is 3rd level or lower, it fails and has no effect. Higher level spells require an ability check."},"Dispel Magic":{name:"Dispel Magic",level:3,school:"Abjuration",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Choose one creature, object, or magical effect within range. Any spell of 3rd level or lower on the target ends. For each spell of 4th level or higher, make an ability check using your spellcasting ability."},"Lightning Bolt":{name:"Lightning Bolt",level:3,school:"Evocation",casting_time:"1 action",range:"Self (100-foot line)",components:["V","S","M"],duration:"Instantaneous",description:"A stroke of lightning forming a line 100 feet long and 5 feet wide blasts out from you in a direction you choose. Each creature in the line must make a Dexterity save or take lightning damage."},"Major Image":{name:"Major Image",level:3,school:"Illusion",casting_time:"1 action",range:"120 feet",components:["V","S","M"],duration:"Concentration, up to 10 minutes",description:"You create the image of an object, a creature, or some other visible phenomenon that is no larger than a 20-foot cube. The image appears at a spot within range and lasts for the duration. The image includes sounds, smells, and temperature."},"Sleet Storm":{name:"Sleet Storm",level:3,school:"Evocation",casting_time:"1 action",range:"150 feet",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"Until the spell ends, freezing rain and sleet fall in a 20-foot-tall cylinder with a 40-foot radius centered on a point you choose within range. The area is heavily obscured, and the ground is difficult terrain."},Telekinesis:{name:"Telekinesis",level:3,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Concentration, up to 1 minute",description:"You gain the ability to move or manipulate creatures or objects by thought. When you cast the spell, and as your action each round for the duration, you can exert your will on one creature or object."},"Dimension Door":{name:"Dimension Door",level:4,school:"Conjuration",casting_time:"1 action",range:"500 feet",components:["V"],duration:"Instantaneous",description:"You teleport yourself from your current location to any other spot within range. You arrive at exactly the spot desired. It can be a place you can see, one you can visualize, or one you can describe by stating distance and direction."},"Greater Invisibility":{name:"Greater Invisibility",level:4,school:"Illusion",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Concentration, up to 1 minute",description:"You or a creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person. Unlike regular invisibility, attacks do not end the spell."},Polymorph:{name:"Polymorph",level:4,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"Transforms a creature into a different beast. The target's game statistics, including mental ability scores, are replaced by the statistics of the chosen beast. It retains its alignment and personality. The creature assumes the hit points of the new form."},Stoneskin:{name:"Stoneskin",level:4,school:"Abjuration",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"The touched creature's skin becomes as hard as stone and covered in rocky growths. The target's AC becomes 15 + its Dexterity modifier (max 23), and it is resistant to nonmagical bludgeoning, piercing, and slashing damage."},"Cone of Cold":{name:"Cone of Cold",level:5,school:"Evocation",casting_time:"1 action",range:"Self (60-foot cone)",components:["V","S","M"],duration:"Instantaneous",description:"A blast of cold air erupts from your hands. Each creature in a 60-foot cone must make a Constitution saving throw. A creature takes 8d8 cold damage on a failed save, or half as much on a successful one."},"Teleportation Circle":{name:"Teleportation Circle",level:5,school:"Conjuration",casting_time:"1 minute",range:"10 feet",components:["V","M"],duration:"1 round",description:"As you cast the spell, you draw a circle on the ground with the chalk. When you complete the casting, a glowing portal opens within the circle. Any creature that enters the portal instantly appears within 5 feet of the destination circle."}},Rt={Wizard:{cantrips:["Acid Splash","Fire Bolt","Light","Mage Hand","Mending","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Feather Fall","Grease","Identify","Mage Armor","Magic Missile","Shield","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Shatter","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image","Sleet Storm","Telekinesis"],4:["Dimension Door","Greater Invisibility","Polymorph","Stoneskin"],5:["Cone of Cold","Teleportation Circle"]}},Sorcerer:{cantrips:["Acid Splash","Fire Bolt","Light","Mage Hand","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Feather Fall","Grease","Mage Armor","Magic Missile","Shield","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Shatter","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image","Sleet Storm"],4:["Dimension Door","Greater Invisibility","Polymorph","Stoneskin"],5:["Cone of Cold","Teleportation Circle"]}},Bard:{cantrips:["Light","Mage Hand","Mending","Message","Prestidigitation","Vicious Mockery"],spells_by_level:{1:["Charm Person","Detect Magic","Disguise Self","Expeditious Retreat","Feather Fall","Grease","Healing Word","Identify","Magic Missile","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image"],4:["Dimension Door","Greater Invisibility","Polymorph"],5:["Teleportation Circle"]}},Cleric:{cantrips:["Light","Mending","Message","Sacred Flame"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Identify","Healing Word","Magic Missile","Shield","Sleep"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Scorching Ray"],3:["Animate Dead","Counterspell","Dispel Magic","Lightning Bolt"],4:["Dimension Door","Polymorph"],5:["Cone of Cold"]}},Druid:{cantrips:["Light","Mending","Prestidigitation"],spells_by_level:{1:["Burning Hands","Cure Wounds","Detect Magic","Expeditious Retreat","Feather Fall","Grease","Healing Word","Identify"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Scorching Ray","Shatter"],3:["Animate Dead","Counterspell","Dispel Magic","Lightning Bolt","Sleet Storm"],4:["Dimension Door","Polymorph"],5:["Cone of Cold","Teleportation Circle"]}},Paladin:{cantrips:[],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Feather Fall","Healing Word","Protection","Shield"],2:["Acid Arrow","Hold Person","Scorching Ray"],3:["Counterspell","Dispel Magic","Lightning Bolt"],4:["Dimension Door","Polymorph"],5:["Cone of Cold"]}},Ranger:{cantrips:[],spells_by_level:{1:["Detect Magic","Expeditious Retreat","Feather Fall","Grease","Identify"],2:["Blur","Hold Person","Invisibility","Misty Step"],3:["Blink","Counterspell","Dispel Magic"],4:["Dimension Door","Polymorph"],5:[]}},Warlock:{cantrips:["Acid Splash","Fire Bolt","Mage Hand","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Grease","Hex","Magic Missile"],2:["Acid Arrow","Darkness","Hold Person","Invisibility","Scorching Ray","Shatter"],3:["Counterspell","Dispel Magic","Fireball","Lightning Bolt"],4:["Dimension Door","Greater Invisibility"],5:["Cone of Cold"]}}},oa={Wizard:{1:{1:2},2:{1:3},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Sorcerer:{1:{1:2},2:{1:3},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Bard:{1:{1:2},2:{1:2,2:0},3:{1:3,2:0},4:{1:3,2:2},5:{1:4,2:2,3:0},6:{1:4,2:2,3:0},7:{1:4,2:2,3:2},8:{1:4,2:2,3:2,4:0},9:{1:4,2:3,3:2,4:0},10:{1:4,2:3,3:2,4:1},11:{1:4,2:3,3:3,4:1},12:{1:4,2:3,3:3,4:1},13:{1:4,2:3,3:3,4:2,5:0},14:{1:4,2:3,3:3,4:2,5:0},15:{1:4,2:3,3:3,4:2,5:1},16:{1:4,2:3,3:3,4:2,5:1},17:{1:4,2:3,3:3,4:2,5:1,6:0},18:{1:4,2:3,3:3,4:3,5:1,6:0},19:{1:4,2:3,3:3,4:3,5:2,6:1},20:{1:4,2:3,3:3,4:3,5:2,6:1}},Cleric:{1:{1:2},2:{1:3},3:{1:3,2:2},4:{1:4,2:2},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Druid:{1:{1:2},2:{1:3},3:{1:3,2:2},4:{1:4,2:2},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Paladin:{1:{1:0},2:{1:2},3:{1:3},4:{1:3},5:{1:4,2:2},6:{1:4,2:2},7:{1:4,2:3},8:{1:4,2:3},9:{1:4,2:3,3:2},10:{1:4,2:3,3:2},11:{1:4,2:3,3:3},12:{1:4,2:3,3:3},13:{1:4,2:3,3:3,4:1},14:{1:4,2:3,3:3,4:1},15:{1:4,2:3,3:3,4:2},16:{1:4,2:3,3:3,4:2},17:{1:4,2:3,3:3,4:3,5:1},18:{1:4,2:3,3:3,4:3,5:1},19:{1:4,2:3,3:3,4:3,5:2},20:{1:4,2:3,3:3,4:3,5:2}},Ranger:{1:{1:0},2:{1:2},3:{1:2,2:0},4:{1:3,2:0},5:{1:3,2:2},6:{1:3,2:2},7:{1:3,2:2,3:0},8:{1:3,2:2,3:0},9:{1:3,2:3,3:0},10:{1:4,2:3,3:0},11:{1:4,2:3,3:2},12:{1:4,2:3,3:2},13:{1:4,2:3,3:2,4:0},14:{1:4,2:3,3:2,4:0},15:{1:4,2:3,3:2,4:1},16:{1:4,2:3,3:2,4:1},17:{1:4,2:3,3:3,4:1},18:{1:4,2:3,3:3,4:1},19:{1:4,2:3,3:3,4:2},20:{1:4,2:3,3:3,4:2}},Warlock:{1:{1:1},2:{1:2},3:{1:2,2:2},4:{1:2,2:2},5:{1:2,2:2,3:2},6:{1:2,2:2,3:2},7:{1:2,2:2,3:2,4:1},8:{1:2,2:2,3:2,4:1},9:{1:2,2:2,3:2,4:2,5:1},10:{1:2,2:2,3:2,4:2,5:1},11:{1:3,2:3,3:3,4:3,5:1},12:{1:3,2:3,3:3,4:3,5:2},13:{1:3,2:3,3:3,4:3,5:2},14:{1:3,2:3,3:3,4:3,5:2},15:{1:3,2:3,3:3,4:3,5:3},16:{1:3,2:3,3:3,4:3,5:3},17:{1:4,2:4,3:4,4:4,5:4},18:{1:4,2:4,3:4,4:4,5:4},19:{1:4,2:4,3:4,4:4,5:4},20:{1:4,2:4,3:4,4:4,5:4}}};function Ea(s){if(s in Ce)return Ce[s];try{const t=R.getInstance().get("classes.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.name===s);if(a){if(a.baseClass&&a.baseClass in Ce){const i=Ce[a.baseClass];return{...i,...a,available_skills:a.available_skills||i.available_skills}}return a}}}catch{}}const ct={1:0,2:300,3:900,4:2700,5:6500,6:14e3,7:23e3,8:34e3,9:48e3,10:64e3,11:85e3,12:1e5,13:12e4,14:14e4,15:165e3,16:195e3,17:225e3,18:265e3,19:305e3,20:355e3},Ma={1:2,2:2,3:2,4:2,5:3,6:3,7:3,8:3,9:4,10:4,11:4,12:4,13:5,14:5,15:5,16:5,17:6,18:6,19:6,20:6},Ps=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],Ae={adjectives:{techno:{bass:["Thumping","Pulsing","Driving","Hypnotic","Relentless","Mechanical","Deep"],treble:["Piercing","Synthetic","Digital","Sharp","Glittering","Laser"],mid:["Driving","Rhythmic","Flowing","Seamless","Kinetic"],quiet:["Minimal","Sparse","Subtle","Ambient","Whispered"],loud:["Pounding","Overwhelming","Massive","Explosive","Crushing"]},rock:{bass:["Heavy","Rumbling","Gritty","Raw","Crushing","Grinding"],treble:["Screaming","Wailing","Soaring","Piercing","Shrill"],mid:["Crunchy","Distorted","Overdriven","Gritty","Aggressive"],quiet:["Acoustic","Stripped","Bare","Intimate","Unplugged"],loud:["Thunderous","Explosive","Deafening","Roaring","Volcanic"]},metal:{bass:["Brutal","Crushing","Punishing","Merciless","Devastating","Obliterating"],treble:["Shredding","Screaming","Hellish","Demonic","Piercing"],mid:["Chugging","Grinding","Relentless","Aggressive","Furious"],quiet:["Doomed","Somber","Ominous","Foreboding","Dark"],loud:["Deafening","Apocalyptic","Cataclysmic","Earth-Shattering","Monolithic"]},ambient:{bass:["Deep","Vast","Profound","Oceanic","Cavernous","Abyssal"],treble:["Ethereal","Crystalline","Shimmering","Celestial","Gossamer"],mid:["Whispering","Drifting","Floating","Meandering","Breathing"],quiet:["Silent","Hushed","Meditative","Peaceful","Tranquil"],loud:["Swelling","Building","Crescendoing","Rising","Expanding"]},classical:{bass:["Grand","Stately","Majestic","Regal","Noble","Profound"],treble:["Soaring","Triumphant","Brilliant","Radiant","Gleaming"],mid:["Noble","Elegant","Refined","Graceful","Poised"],quiet:["Gentle","Delicate","Tender","Soft","Intimate"],loud:["Majestic","Powerful","Commanding","Heroic","Epic"]},jazz:{bass:["Smooth","Groovy","Walking","Swinging","Funky","Mellow"],treble:["Bright","Crisp","Brilliant","Clear","Sparkling"],mid:["Swinging","Syncopated","Bluesy","Soulful","Improvisational"],quiet:["Cool","Laid-back","Subtle","Understated","Intimate"],loud:["Big","Bold","Brassy","Energetic","Vibrant"]},"hip hop":{bass:["Bumping","Knocking","Booming","Heavy","Fat","Deep"],treble:["Sharp","Crisp","Cutting","Bright","Piercing"],mid:["Flowing","Smooth","Rhythmic","Funky","Grooving"],quiet:["Chill","Mellow","Lo-fi","Smooth","Laid-back"],loud:["Hype","Aggressive","Hard","Explosive","Banging"]},pop:{bass:["Bouncy","Punchy","Groovy","Catchy","Upbeat"],treble:["Sparkling","Bright","Shimmering","Glittering","Polished"],mid:["Catchy","Memorable","Infectious","Singable","Hooky"],quiet:["Soft","Gentle","Sweet","Tender","Intimate"],loud:["Anthemic","Soaring","Epic","Triumphant","Powerful"]},electronic:{bass:["Pulsing","Wobbling","Throbbing","Synthetic","Digital","Modulated"],treble:["Glitchy","Digital","Futuristic","Synthetic","Robotic"],mid:["Synthetic","Programmed","Sequenced","Processed","Modular"],quiet:["Atmospheric","Ambient","Minimal","Spacious","Ethereal"],loud:["Massive","Crushing","Drop-heavy","Explosive","Wall-of-sound"]},default:{bass:["Booming","Deep","Low","Resonant","Rumbling","Heavy"],treble:["Sharp","High","Piercing","Bright","Clear"],mid:["Resonant","Balanced","Full","Rich","Warm"],quiet:["Quiet","Soft","Gentle","Subtle","Hushed"],loud:["Loud","Powerful","Strong","Intense","Forceful"]}},descriptors:["Swift","Iron","Shadow","Mystic","Wild","Radiant","Grim","Noble","Ancient","Blazing"],classAspects:{Barbarian:["Warrior","Rage","Beast","Fury"],Bard:["Song","Voice","Muse","Melody"],Cleric:["Light","Faith","Devotion","Grace"],Druid:["Wild","Nature","Grove","Fang"],Fighter:["Blade","Shield","Defender","Champion"],Monk:["Fist","Spirit","Flow","Path"],Paladin:["Oath","Justice","Light","Defender"],Ranger:["Hunter","Arrow","Tracker","Scout"],Rogue:["Shadow","Blade","Whisper","Dagger"],Sorcerer:["Flame","Storm","Chaos","Power"],Warlock:["Pact","Shadow","Hex","Dark"],Wizard:["Arcane","Sage","Spellweaver","Mind"],Artificer:["Craft","Forge","Construct","Invention"]},prefixes:["Thunder","Shadow","Flame","Frost","Storm","Star","Blood","Soul","Moon","Sun"],suffixes:["Blessed","Forged","Wreathed","Touched","Born","Kissed","Bound","Marked","Sworn","Woven"],occupations:["smith","weaver","caller","keeper","herald","warden","seeker","walker","singer"],realms:["Eternal Stage","Forgotten Hall","Misty Vale","Iron Keep","Crystal Spire","Shadow Court","Golden Fields","Storm Peak","Ancient Grove","Silent Deep"],subtitlePrefixes:["of Stars","Eternal","Reborn","Unchained","Ascendant","the Fallen","Unbound","Rising","Triumphant","Immortal"]},Wr={athletics:"STR",acrobatics:"DEX",sleight_of_hand:"DEX",stealth:"DEX",arcana:"INT",history:"INT",investigation:"INT",nature:"INT",religion:"INT",animal_handling:"WIS",insight:"WIS",medicine:"WIS",perception:"WIS",survival:"WIS",deception:"CHA",intimidation:"CHA",performance:"CHA",persuasion:"CHA"};function la(s){if(s in Rt)return Rt[s];try{const e=R.getInstance(),t=`classSpellLists.${s}`,a=e.get(t);if(a&&a.length>0)return a[0]}catch{}}function Da(s,e){if(s in oa){const t=oa[s];if(t&&e in t)return t[e]}try{const i=R.getInstance().get("classSpellSlots");if(i&&Array.isArray(i)){const n=i.find(r=>r.class===s);if(n&&n.slots_by_level){const r=n.slots_by_level;if(e in r)return r[e]}}}catch{}}const zr=10,xs=50;class Ie{constructor(){this.allSpellsCache=null,this.levelCache=null,this.schoolCache=null,this.manager=R.getInstance()}static getInstance(){return Ie.instance||(Ie.instance=new Ie),Ie.instance}invalidateCache(){this.allSpellsCache=null,this.levelCache=null,this.schoolCache=null}getSpell(e){return this.getSpells().find(a=>a.id===e)}getSpells(){if(!this.allSpellsCache){const e=this.manager.get("spells");this.allSpellsCache=e}return this.allSpellsCache}getSpellsByLevel(e){if(!this.levelCache){this.levelCache=new Map;const t=this.getSpells();for(const a of t)this.levelCache.has(a.level)||this.levelCache.set(a.level,[]),this.levelCache.get(a.level).push(a)}return this.levelCache.get(e)||[]}getSpellsBySchool(e){if(!this.schoolCache){this.schoolCache=new Map;const t=this.getSpells();for(const a of t)this.schoolCache.has(a.school)||this.schoolCache.set(a.school,[]),this.schoolCache.get(a.school).push(a)}return this.schoolCache.get(e)||[]}getSpellsForClass(e){return this.getSpells().filter(a=>a.classes&&a.classes.includes(e))}getAvailableSpells(e){const t=this.getSpells(),a=[];for(const i of t)this.validatePrerequisites(i,e).valid&&a.push(i);return a}getClassSpellList(e){const t=`spells.${e}`;return this.manager.get(t)||[]}getSpellSlotsForClass(e,t){const a=Da(e,t);return a?Object.values(a).reduce((n,r)=>n+r,0):0}validatePrerequisites(e,t){return De.validateSpellPrerequisites(e.prerequisites,t)}validateSpell(e){return De.validateSpell(e)}hasSpell(e){return this.getSpell(e)!==void 0}getSpellCount(){return this.getSpells().length}getSpellsBySource(e){return this.getSpells().filter(t=>t.source===e)}getQueryStats(){const e=this.getSpells(),t=e.filter(o=>o.source==="default"),a=e.filter(o=>o.source==="custom"),i={};for(let o=0;o<=9;o++)i[o]=this.getSpellsByLevel(o).length;const n={Abjuration:this.getSpellsBySchool("Abjuration").length,Conjuration:this.getSpellsBySchool("Conjuration").length,Divination:this.getSpellsBySchool("Divination").length,Enchantment:this.getSpellsBySchool("Enchantment").length,Evocation:this.getSpellsBySchool("Evocation").length,Illusion:this.getSpellsBySchool("Illusion").length,Necromancy:this.getSpellsBySchool("Necromancy").length,Transmutation:this.getSpellsBySchool("Transmutation").length},r=new Set;for(const o of e)if(o.classes)for(const l of o.classes)r.add(l);return{totalSpells:e.length,defaultSpells:t.length,customSpells:a.length,spellsByLevel:i,spellsBySchool:n,classesWithSpells:r.size}}}function qr(){return Ie.getInstance()}class R{constructor(){this.defaultData=new Map,this.extensions=new Map,this.customWeights=new Map,this.imageOverrides=new Map}static getInstance(){return R.instance||(R.instance=new R),R.instance}initializeDefaults(e,t){this.defaultData.set(e,[...t]),this.invalidateRegistryCache(e)}initializeAllDefaults(e){for(const[t,a]of Object.entries(e))this.initializeDefaults(t,a)}register(e,t,a={}){const i=this.extensions.get(e),n=a.mode!==void 0?a.mode:i?.options.mode,{weights:r={},validate:o=!0}=a,l=["races","classes"];let c=t;if(l.includes(e)&&(c=t.map(d=>typeof d=="string"?d:d&&typeof d=="object"&&typeof d.name=="string"?d.name:d)),o){const d=this.validate(e,c);if(!d.valid)throw new Error(`Invalid items for category '${e}':
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./unifyBeatMap-DIsNlck4.cjs"),z=require("./arweaveGatewayManager-C9SIMDTd.cjs"),B=require("./gateway-DUk4nCao.cjs"),ds={0:"",1:"I",2:"II",3:"III",4:"IV",5:"V",6:"VI",7:"VII",8:"VIII",9:"IX",10:"X"},st=10,hs=10,ps=1e3,ia=1.5;function tr(s){return typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=10}function ms(s){return Math.max(0,Math.min(10,Math.floor(s)))}function ar(s){return typeof s=="string"&&["normal","aggressive"].includes(s)}const Ze={Greataxe:{name:"Greataxe",type:"weapon",rarity:"common",weight:7,damage:{dice:"1d12",damageType:"slashing"},weaponProperties:["two-handed"],spawnWeight:1,tags:["martial","melee","two-handed"],description:"A massive two-handed axe favored by barbarians for its devastating damage potential. Requires great strength to wield effectively."},Longsword:{name:"Longsword",type:"weapon",rarity:"common",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["versatile"],spawnWeight:1,tags:["martial","melee","versatile"],description:"A versatile sword usable with one or two hands. The classic weapon of knights and warriors throughout history."},Shortsword:{name:"Shortsword",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"piercing",versatile:"1d8"},weaponProperties:["finesse","light","versatile"],spawnWeight:1,tags:["martial","melee","finesse","light"],description:"A light, agile sword effective in close quarters. Can be used with Dexterity instead of Strength when wielded alone."},Rapier:{name:"Rapier",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["finesse"],spawnWeight:1,tags:["martial","melee","finesse"],description:"A slender, thrusting sword designed for speed and precision. Favored by duelists and rogues for its finesse property."},Quarterstaff:{name:"Quarterstaff",type:"weapon",rarity:"common",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile","two-handed"],spawnWeight:1,tags:["simple","melee","versatile"],description:"A simple wooden staff, often the first weapon of monks and spellcasters. Effective when used with two hands."},Mace:{name:"Mace",type:"weapon",rarity:"common",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile"],spawnWeight:1,tags:["simple","melee","versatile"],description:"A simple blunt weapon effective against armored foes. Common among clerics and those favoring divine righteousness."},Handaxe:{name:"Handaxe",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"slashing"},weaponProperties:["light","thrown","range_20_60"],spawnWeight:1,tags:["simple","melee","thrown","light"],description:"A small axe versatile in melee or as a thrown weapon. Popular among dwarves and rangers."},Dagger:{name:"Dagger",type:"weapon",rarity:"common",weight:1,damage:{dice:"1d4",damageType:"piercing",versatile:"1d6"},weaponProperties:["finesse","light","thrown","range_20_60"],spawnWeight:1,tags:["simple","melee","finesse","light","thrown"],description:"A lightweight weapon useful for melee and ranged attacks. Essential for rogues and as a backup weapon for all adventurers."},Dart:{name:"Dart",type:"weapon",rarity:"common",weight:.25,damage:{dice:"1d4",damageType:"piercing"},weaponProperties:["finesse","thrown","range_20_60"],spawnWeight:1,tags:["simple","ranged","thrown","finesse"],description:"A small throwing weapon, often carried in bulk. Lightweight but deals minimal damage."},Javelin:{name:"Javelin",type:"weapon",rarity:"common",weight:2,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["thrown","range_30_120"],spawnWeight:1,tags:["simple","melee","thrown"],description:"A light spear designed for throwing. Effective at both melee and range."},"Light Crossbow":{name:"Light Crossbow",type:"weapon",rarity:"uncommon",weight:5,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["ammunition","range_80_320","two-handed","loading"],spawnWeight:1,tags:["simple","ranged","two-handed","ammunition"],description:"A compact crossbow requiring minimal training to use. Devastating at range but requires a reload action between shots."},"Hand Crossbow":{name:"Hand Crossbow",type:"weapon",rarity:"uncommon",weight:3,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["ammunition","range_30_120","light","loading"],spawnWeight:1,tags:["martial","ranged","light","ammunition"],description:"A small crossbow usable in one hand. Popular among rogues and those who need a ranged sidearm."},Longbow:{name:"Longbow",type:"weapon",rarity:"uncommon",weight:2,damage:{dice:"1d8",damageType:"piercing"},weaponProperties:["ammunition","range_150_600","two-handed","heavy"],spawnWeight:1,tags:["martial","ranged","two-handed","ammunition","heavy"],description:"A tall bow with impressive range and power. Requires significant strength to draw and training to use effectively."},"Martial Melee Weapon":{name:"Martial Melee Weapon",type:"weapon",rarity:"common",weight:3,damage:{dice:"1d8",damageType:"slashing"},weaponProperties:["versatile"],spawnWeight:.5,tags:["martial","melee","versatile","generic"],description:"A generic martial melee weapon. Used as a fallback when no specific weapon is specified."},"No Armor":{name:"No Armor",type:"armor",rarity:"common",weight:0,acBonus:10,spawnWeight:1,tags:["armor","light","no_armor"],description:"No armor worn. Relies entirely on Dexterity for defense. Maximum mobility but minimal protection."},"Leather Armor":{name:"Leather Armor",type:"armor",rarity:"common",weight:10,acBonus:11,properties:[{type:"passive_modifier",target:"ac",value:"11 + DEX",description:"Base AC: 11 + DEX"}],spawnWeight:1,tags:["armor","light"],description:"Light armor made from cured animal hides. Offers basic protection without restricting movement. Adds full Dexterity bonus to AC."},"Scale Mail":{name:"Scale Mail",type:"armor",rarity:"common",weight:45,acBonus:14,properties:[{type:"passive_modifier",target:"ac",value:"14 + min(DEX, 2)",description:"Base AC: 14 + DEX (max 2)"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"}],spawnWeight:1,tags:["armor","medium"],description:"Armor consisting of many overlapping metal scales. Solid protection but imposes disadvantage on Stealth checks."},"Chain Mail":{name:"Chain Mail",type:"armor",rarity:"common",weight:55,acBonus:16,properties:[{type:"passive_modifier",target:"ac",value:"16",description:"Fixed AC: 16"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:13,description:"Requires STR 13"}],spawnWeight:1,tags:["armor","heavy"],description:"Interlocking metal rings provide excellent protection. Requires Strength 13 to wear and imposes disadvantage on Stealth."},"Plate Armor":{name:"Plate Armor",type:"armor",rarity:"rare",weight:65,acBonus:18,properties:[{type:"passive_modifier",target:"ac",value:"18",description:"Fixed AC: 18"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:15,description:"Requires STR 15"}],spawnWeight:1,tags:["armor","heavy","rare"],description:"The finest armor available, consisting of shaped and fitted metal plates. Provides maximum protection but is expensive, heavy, and requires Strength 15."},Shield:{name:"Shield",type:"armor",rarity:"common",weight:6,acBonus:2,properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus"}],spawnWeight:1,tags:["armor","shield"],description:"A portable defensive barrier carried in one hand. Grants +2 to Armor Class. Cannot be used with two-handed weapons."},Spellbook:{name:"Spellbook",type:"item",rarity:"uncommon",weight:3,spawnWeight:1,tags:["gear","arcane","spellbook"],description:"A blank tome used by wizards to record their spells. Essential for preparing and casting wizard spells."},"Holy Symbol":{name:"Holy Symbol",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","divine","focus"],description:"An emblem of a deity used by clerics and paladins as a divine focus for spellcasting."},"Arcane Focus":{name:"Arcane Focus",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","arcane","focus"],description:"An object such as a crystal, orb, or wand used to channel arcane magic. Replaces material components for most spells."},"Druidic Focus":{name:"Druidic Focus",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","druid","focus"],description:"A natural object such as a staff, wand, or totem used by druids to channel primal magic."},"Component Pouch":{name:"Component Pouch",type:"box",rarity:"common",weight:2,spawnWeight:1,tags:["gear","components","spellcasting"],boxContents:{drops:[],consumeOnOpen:!1},description:"A pouch containing small compartments for storing material components needed for spells. A practical alternative to an arcane focus. Assumes to contain all material components without specific gold costs."},Lute:{name:"Lute",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","instrument","bard"],description:"A stringed musical instrument used by bards to perform and channel magical inspiration."},"Thieves' Tools":{name:"Thieves' Tools",type:"item",rarity:"uncommon",weight:1,grantsSkills:[{skillId:"thieves_tools",level:"proficient"}],spawnWeight:1,tags:["gear","tools","dexterity"],description:"A small collection of tools including picks, files, and crowbar. Essential for picking locks and disarming traps."},"Healer's Kit":{name:"Healer's Kit",type:"box",rarity:"common",weight:3,spawnWeight:1,tags:["gear","healing","consumable"],boxContents:{drops:[{pool:[{weight:100,itemName:"Medical Supply",quantity:10}]}],consumeOnOpen:!1},description:"A collection of bandages, salves, and tools for treating wounds. Contains 10 medical supplies. Essential for stabilizing dying creatures and providing medical aid."},Bedroll:{name:"Bedroll",type:"item",rarity:"common",weight:7,spawnWeight:1,tags:["gear","camp","comfort"],description:"A padded blanket roll used for sleeping outdoors. Essential comfort for adventurers on the road."},Rope:{name:"Rope",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","utility","climbing"],description:"50 feet of hempen rope. Essential for climbing, securing prisoners, and countless other utility purposes."},Backpack:{name:"Backpack",type:"box",rarity:"common",weight:5,spawnWeight:1,tags:["gear","container","storage"],boxContents:{drops:[],consumeOnOpen:!1},description:"A leather backpack for carrying adventuring gear. Essential storage for any traveling adventurer."},Torch:{name:"Torch",type:"item",rarity:"common",weight:1,properties:[{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],spawnWeight:1,tags:["gear","light","consumable"],description:"A wooden rod topped with flammable material. Burns for about an hour, shedding light in a 20-foot radius."},Waterskin:{name:"Waterskin",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","container","water"],description:"A leather container for holding water. Essential for surviving in wilderness and dungeon environments."},"Ink & Quill":{name:"Ink & Quill",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","writing","consumable"],description:"Ink and a quill pen for writing. Useful for recording maps, copying spells, and documenting adventures."},"Ball Bearings":{name:"Ball Bearings",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","utility","rogue"],description:"A bag containing 1,000 tiny metal balls. Scattered on the ground, they create a treacherous surface that can cause creatures to fall prone."},String:{name:"String",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","utility"],description:"10 feet of sturdy string. Useful for tripwires, tying items together, or countless other creative purposes."},Bell:{name:"Bell",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","utility","alarm"],description:"A small brass bell that rings when disturbed. Perfect for creating warning systems or signaling."},Crowbar:{name:"Crowbar",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","tool","utility"],description:"A heavy iron bar used for prying open doors, chests, and other stuck objects. Grants advantage on Strength checks to force objects open."},Hammer:{name:"Hammer",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","tool","utility"],description:"A sturdy hammer designed for driving pitons into walls and other heavy work. Essential for dungeon exploration."},Piton:{name:"Piton",type:"item",rarity:"common",weight:.25,spawnWeight:1,tags:["gear","climbing","utility"],description:"A metal spike with a ring at one end. Driven into cracks to create climbing anchors or secure rope lines."},"Small Knife":{name:"Small Knife",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","tool","utility"],description:"A small utility knife useful for cutting rope, skinning game, and other everyday tasks. Not designed for combat."},"Iron Key":{name:"Iron Key",type:"item",rarity:"common",weight:0,spawnWeight:.5,tags:["key","utility","consumable"],description:"A simple iron key that can unlock certain containers. Consumed when used to open a locked box."},"Golden Key":{name:"Golden Key",type:"item",rarity:"uncommon",weight:0,spawnWeight:.2,tags:["key","utility","consumable","valuable"],description:"An ornate golden key for unlocking valuable chests. Consumed when used to open a locked box."},"Skeleton Key":{name:"Skeleton Key",type:"item",rarity:"rare",weight:0,spawnWeight:.1,tags:["key","utility","consumable","universal"],description:"A master key that can open many different locks. Consumed when used to open a locked box."},Lockpick:{name:"Lockpick",type:"item",rarity:"common",weight:0,spawnWeight:.8,tags:["gear","rogue","consumable","tool"],description:"A simple lockpick. Multiple may be needed for complex locks, as they often break during use."},"Gold Coin":{name:"Gold Coin",type:"item",rarity:"common",weight:0,spawnWeight:0,tags:["currency","gold","money"],description:"A standard gold coin used as currency throughout the realm. Can be consumed to open certain magical locks."},"Locked Chest":{name:"Locked Chest",type:"box",rarity:"uncommon",weight:10,spawnWeight:.5,tags:["loot","treasure","locked"],boxContents:{openRequirements:[{itemName:"Iron Key"}],drops:[{pool:[{weight:100,gold:50}]},{pool:[{weight:50,itemName:"Shortsword"},{weight:30,itemName:"Leather Armor"},{weight:20,itemName:"Medical Supply",quantity:3}]}]},description:"A sturdy locked chest. Requires an Iron Key to open."},"Gilded Strongbox":{name:"Gilded Strongbox",type:"box",rarity:"rare",weight:15,spawnWeight:.3,tags:["loot","treasure","gold-locked"],boxContents:{openRequirements:[{itemName:"Gold Coin",quantity:100}],drops:[{pool:[{weight:100,gold:250}]},{pool:[{weight:40,itemName:"Longsword"},{weight:30,itemName:"Chain Mail"},{weight:20,itemName:"Scale Mail"},{weight:10,itemName:"Medical Supply",quantity:5}]}]},description:"A gilded strongbox with a magical lock. Consumes 100 Gold Coins to unlock."},"Royal Treasury Box":{name:"Royal Treasury Box",type:"box",rarity:"very_rare",weight:20,spawnWeight:.1,tags:["loot","treasure","royal","locked"],boxContents:{openRequirements:[{itemName:"Golden Key"},{itemName:"Gold Coin",quantity:200}],drops:[{pool:[{weight:100,gold:1e3}]},{pool:[{weight:30,itemName:"Plate Armor"},{weight:25,itemName:"Chain Mail"},{weight:25,itemName:"Greataxe"},{weight:20,itemName:"Longsword"}]},{pool:[{weight:60,itemName:"Medical Supply",quantity:10},{weight:40,itemName:"Thieves' Tools"}]}]},description:"A royal treasury box sealed with powerful magic. Requires a Golden Key and 200 Gold Coins to open."},"Thieves' Cache":{name:"Thieves' Cache",type:"box",rarity:"uncommon",weight:5,spawnWeight:.4,tags:["loot","treasure","rogue","locked"],boxContents:{openRequirements:[{itemName:"Lockpick",quantity:3}],drops:[{pool:[{weight:100,gold:75}]},{pool:[{weight:50,itemName:"Thieves' Tools"},{weight:30,itemName:"Dagger"},{weight:20,itemName:"Disguise Kit"}]}]},description:"A hidden cache with a complex lock. Requires 3 lockpicks to crack (consumed in the process)."},Candle:{name:"Candle",type:"item",rarity:"common",weight:0,properties:[{type:"special_property",target:"light",value:"dim_light_5ft",description:"Sheds dim light 5ft, bright 2ft"}],spawnWeight:1,tags:["gear","light","consumable"],description:"A simple wax candle that burns for about an hour. Provides dim light in a 5-foot radius, bright light in a 2-foot radius."},"Hooded Lantern":{name:"Hooded Lantern",type:"item",rarity:"common",weight:2,properties:[{type:"special_property",target:"light",value:"bright_light_30ft",description:"Sheds bright light 30ft, dim 30ft when unhooded"}],spawnWeight:1,tags:["gear","light"],description:"A metal lantern with a hood that can be raised or lowered to control the light. Burns for 6 hours on a flask of oil. Sheds bright light in a 30-foot cone and dim light for an additional 30 feet when unhooded."},"Oil Flask":{name:"Oil Flask",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","light","consumable"],description:"A flask of oil suitable for fueling lanterns. Can also be used as a weapon when thrown and ignited, dealing fire damage. Burns for 6 hours in a lantern."},Rations:{name:"Rations",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","food","consumable"],description:"One day's worth of dried meat, hard bread, and other preserved food. Essential for surviving long journeys without access to fresh food."},Water:{name:"Water",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","drink","consumable"],description:"Clean drinking water essential for survival. Typically stored in a waterskin and consumed during rest periods."},"Medical Supply":{name:"Medical Supply",type:"item",rarity:"common",weight:.3,spawnWeight:1,tags:["gear","healing","consumable"],description:"A single-use bandage, salve, or medical tool for treating wounds. Used to stabilize dying creatures or provide basic first aid."},Tinderbox:{name:"Tinderbox",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","fire","utility"],description:"A small container with flint, steel, and tinder for starting fires. Essential for lighting torches, campfires, and lanterns."},"Mess Kit":{name:"Mess Kit",type:"item",rarity:"common",weight:1,spawnWeight:1,tags:["gear","camp","dining"],description:"A tin box containing a cup, bowl, and simple eating utensils. Collapses flat for easy packing and storage."},Blanket:{name:"Blanket",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","camp","comfort"],description:"A warm woolen blanket for sleeping. Provides basic warmth and comfort during rest in cold environments."},Costume:{name:"Costume",type:"item",rarity:"common",weight:4,spawnWeight:1,tags:["gear","clothing","performance"],description:"A set of performer's clothing suitable for stage appearances and entertaining. Includes colorful fabrics and decorative elements."},"Traveler's Clothes":{name:"Traveler's Clothes",type:"item",rarity:"common",weight:4,spawnWeight:1,tags:["gear","clothing"],description:"Sturdy, practical clothing designed for long journeys. Includes boots, a cloak, and weather-appropriate garb for the road."},Vestments:{name:"Vestments",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","clothing","religious"],description:"Ceremonial robes worn by priests and other religious figures. Often adorned with symbols of faith and made of fine materials."},"Alms Box":{name:"Alms Box",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","religious","container"],description:"A small wooden box with a slot for collecting alms and donations. Used by priests and mendicants to gather charitable offerings."},Incense:{name:"Incense",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","religious","consumable"],description:"A block of aromatic incense used in religious ceremonies and rituals. Burns slowly to produce fragrant smoke during prayers and worship."},Censer:{name:"Censer",type:"item",rarity:"common",weight:2,spawnWeight:1,tags:["gear","religious"],description:"A metal container suspended on chains, used for burning incense during religious ceremonies. Often made of silver or brass and adorned with religious symbols."},"Book of Lore":{name:"Book of Lore",type:"item",rarity:"common",weight:5,spawnWeight:1,tags:["gear","book","knowledge"],description:"A scholarly tome containing accumulated knowledge on various subjects. Scholars and wizards often carry these to reference arcane theories, historical accounts, or arcane formulas."},"Bottle of Ink":{name:"Bottle of Ink",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","consumable"],description:"A small glass bottle containing black ink, sealed with a stopper. Used with an ink pen for writing on parchment or paper."},"Ink Pen":{name:"Ink Pen",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing"],description:"A wooden pen with a metal nib, designed to be dipped in ink for writing. Essential for scribes, scholars, and anyone needing to keep written records."},Parchment:{name:"Parchment",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","consumable"],description:"A single sheet of parchment made from animal skin, used for writing important documents, letters, or arcane inscriptions. More durable than paper but also more expensive."},"Bag of Sand":{name:"Bag of Sand",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","writing","utility"],description:"A small cloth bag filled with fine sand. Sprinkled over freshly written text to absorb excess ink and speed drying, preventing smudges and blots."},"Disguise Kit":{name:"Disguise Kit",type:"item",rarity:"common",weight:3,spawnWeight:1,tags:["gear","deception","rogue"],description:"A kit containing cosmetics, hair dye, and small props to create disguises. Includes makeup, false facial hair, and other materials needed to change one's appearance. Essential for spies, actors, and rogues who need to assume different identities."},"Burglar's Pack":{name:"Burglar's Pack",type:"box",rarity:"common",weight:44,spawnWeight:1,tags:["gear","pack","rogue"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Ball Bearings",quantity:1e3}]},{pool:[{weight:100,itemName:"String"}]},{pool:[{weight:100,itemName:"Bell"}]},{pool:[{weight:100,itemName:"Candle",quantity:5}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Hooded Lantern"}]},{pool:[{weight:100,itemName:"Oil Flask",quantity:2}]},{pool:[{weight:100,itemName:"Rations",quantity:5}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack containing gear for a rogue: a backpack, a bag of 1,000 ball bearings, 10 feet of string, a bell, 5 candles, a crowbar, a hammer, 10 pitons, a hooded lantern, 2 flasks of oil, 5 days' rations, a tinderbox, and a waterskin. Includes a pack with 50 feet of hempen rope strapped to the side."},"Explorer's Pack":{name:"Explorer's Pack",type:"box",rarity:"common",weight:59,spawnWeight:1,tags:["gear","pack","general"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Mess Kit"}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]}],consumeOnOpen:!0},description:"A backpack containing gear for wilderness exploration: a backpack, a bedroll, mess kit, tinderbox, 10 torches, and 10 days' rations. Includes a waterskin and 50 feet of hempen rope."},"Entertainer's Pack":{name:"Entertainer's Pack",type:"box",rarity:"common",weight:58,spawnWeight:1,tags:["gear","pack","bard"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Costume",quantity:2}]},{pool:[{weight:100,itemName:"Candle",quantity:5}]},{pool:[{weight:100,itemName:"Rations",quantity:5}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Disguise Kit"}]}],consumeOnOpen:!0},description:"A backpack containing a bard's performance gear: a backpack, a bedroll, 2 costumes, 5 candles, 5 days' rations, a waterskin, and a disguise kit."},"Priest's Pack":{name:"Priest's Pack",type:"box",rarity:"common",weight:33,spawnWeight:1,tags:["gear","pack","cleric"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Blanket"}]},{pool:[{weight:100,itemName:"Candle",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Alms Box"}]},{pool:[{weight:100,itemName:"Incense",quantity:2}]},{pool:[{weight:100,itemName:"Censer"}]},{pool:[{weight:100,itemName:"Vestments"}]},{pool:[{weight:100,itemName:"Rations",quantity:2}]},{pool:[{weight:100,itemName:"Waterskin"}]}],consumeOnOpen:!0},description:"A backpack containing a priest's supplies: a backpack, a blanket, 10 candles, a tinderbox, an alms box, 2 blocks of incense, a censer, vestments, 2 days' rations, and a waterskin."},"Dungeon Delver's Pack":{name:"Dungeon Delver's Pack",type:"box",rarity:"common",weight:48,spawnWeight:1,tags:["gear","pack","dungeon"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack optimized for dungeon exploration. Contains a backpack, crowbar, hammer, 10 pitons, 10 torches, a tinderbox, 10 days' rations, and a waterskin. Includes 50 feet of hempen rope."},"Dungeoneer's Pack":{name:"Dungeoneer's Pack",type:"box",rarity:"common",weight:48,spawnWeight:1,tags:["gear","pack","dungeon"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Crowbar"}]},{pool:[{weight:100,itemName:"Hammer"}]},{pool:[{weight:100,itemName:"Piton",quantity:10}]},{pool:[{weight:100,itemName:"Torch",quantity:10}]},{pool:[{weight:100,itemName:"Tinderbox"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack optimized for underground exploration. Contains a backpack, crowbar, hammer, 10 pitons, 10 torches, a tinderbox, 10 days' rations, and a waterskin. Includes 50 feet of hempen rope."},"Scholar's Pack":{name:"Scholar's Pack",type:"box",rarity:"common",weight:49,spawnWeight:1,tags:["gear","pack","wizard"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Book of Lore"}]},{pool:[{weight:100,itemName:"Bottle of Ink"}]},{pool:[{weight:100,itemName:"Ink Pen"}]},{pool:[{weight:100,itemName:"Parchment",quantity:10}]},{pool:[{weight:100,itemName:"Bag of Sand"}]}],consumeOnOpen:!0},description:"A backpack containing a scholar's supplies: a backpack, a book of lore, a bottle of ink, an ink pen, 10 sheets of parchment, and a little bag of sand."},"Traveler's Pack":{name:"Traveler's Pack",type:"box",rarity:"common",weight:43,spawnWeight:1,tags:["gear","pack","general"],boxContents:{drops:[{pool:[{weight:100,itemName:"Backpack"}]},{pool:[{weight:100,itemName:"Bedroll"}]},{pool:[{weight:100,itemName:"Mess Kit"}]},{pool:[{weight:100,itemName:"Rations",quantity:10}]},{pool:[{weight:100,itemName:"Waterskin"}]},{pool:[{weight:100,itemName:"Traveler's Clothes"}]},{pool:[{weight:100,itemName:"Rope"}]}],consumeOnOpen:!0},description:"A backpack for long-distance travel: a backpack, a bedroll, a mess kit, 10 days' rations, a waterskin, traveler's clothes, and 50 feet of hempen rope."},Arrow:{name:"Arrow",type:"item",rarity:"common",weight:.05,spawnWeight:1,tags:["ammunition","bow"],description:"Ammunition for a bow. Typically carried in quivers of 20 arrows. Essential for archers."},Bolt:{name:"Bolt",type:"item",rarity:"common",weight:.075,spawnWeight:1,tags:["ammunition","crossbow"],description:"Ammunition for a crossbow. Shorter and heavier than arrows, designed for crossbow use."},Insignia:{name:"Insignia",type:"item",rarity:"common",weight:0,spawnWeight:1,tags:["gear","roleplay","insignia"],description:"A badge, banner, or token representing rank, membership, or authority. Useful for roleplaying and establishing credentials."},"Martial Arts":{name:"Martial Arts",type:"weapon",rarity:"common",weight:0,damage:{dice:"1d4",damageType:"bludgeoning",versatile:"1d6"},weaponProperties:["finesse","unarmed"],spawnWeight:1,tags:["monk","unarmed","natural"],description:"The monk's unarmed strike. Uses Dexterity instead of Strength and deals increased damage at higher levels."}};class U{static rollDie(e){if(e<1)throw new Error("Die must have at least 1 side");return Math.floor(Math.random()*e)+1}static rollMultipleDice(e,t){const a=[];for(let i=0;i<e;i++)a.push(this.rollDie(t));return a}static parseDiceFormula(e){const t=e.match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!t)throw new Error(`Invalid dice formula: ${e}`);const a=parseInt(t[1],10),i=parseInt(t[2],10),n=t[3]?parseInt(t[3],10):0,r=this.rollMultipleDice(a,i),o=r.reduce((l,c)=>l+c,0)+n;return{diceCount:a,diceSides:i,modifier:n,rolls:r,total:o}}static rollD20(){return this.rollDie(20)}static rollWithAdvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.max(e,t)}}static rollWithDisadvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.min(e,t)}}static rollInitiative(e){return this.rollD20()+e}static isCriticalHit(e){return e===20}static isCriticalMiss(e){return e===1}static doubleDamage(e){return[...e,...e]}static calculateDamage(e,t,a=!1){let n=this.parseDiceFormula(e).rolls;a&&(n=this.doubleDamage(n));const r=n.reduce((o,l)=>o+l,0)+t;return{diceFormula:e,rolls:n,modifier:t,total:r,isCritical:a}}static rollSavingThrow(e,t=0){return this.rollD20()+e+t}static rollAbilityCheck(e,t=0){return this.rollD20()+e+t}static seededRoll(e){const t=Math.sin(e)*1e4;return Math.floor((t-Math.floor(t))*20)+1}static rollPercentile(){return this.rollDie(100)}}const ir={1:[0,2,0,0,0,0,0,0,0,0],2:[0,3,0,0,0,0,0,0,0,0],3:[0,4,2,0,0,0,0,0,0,0],4:[0,4,3,0,0,0,0,0,0,0],5:[0,4,3,2,0,0,0,0,0,0],6:[0,4,3,3,0,0,0,0,0,0],7:[0,4,3,3,1,0,0,0,0,0],8:[0,4,3,3,2,0,0,0,0,0],9:[0,4,3,3,3,1,0,0,0,0],10:[0,4,3,3,3,2,0,0,0,0],11:[0,4,3,3,3,2,1,0,0,0],12:[0,4,3,3,3,2,1,0,0,0],13:[0,4,3,3,3,2,1,1,0,0],14:[0,4,3,3,3,2,1,1,0,0],15:[0,4,3,3,3,2,1,1,1,0],16:[0,4,3,3,3,2,1,1,1,0],17:[0,4,3,3,3,2,1,1,1,1],18:[0,4,3,3,3,3,1,1,1,1],19:[0,4,3,3,3,3,2,1,1,1],20:[0,4,3,3,3,3,2,2,1,1]};function sa(s){const e=ir[s];if(!e)return{};const t={};for(let a=1;a<e.length;a++)e[a]>0&&(t[a]=e[a]);return t}const di={charm:{name:"Charmed",requiresConcentration:!0,mechanicalEffects:{disadvantageOnAttackNonSource:!0}},frighten:{name:"Frightened",requiresConcentration:!0,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},stun:{name:"Stunned",requiresConcentration:!1,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0,skipTurn:!0}},paralyze:{name:"Paralyzed",requiresConcentration:!1,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0,skipTurn:!0}},restrain:{name:"Restrained",requiresConcentration:!0,mechanicalEffects:{disadvantageOnDexSaves:!0,speedZero:!0}},poison:{name:"Poisoned",requiresConcentration:!1,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},blind:{name:"Blinded",requiresConcentration:!1,mechanicalEffects:{disadvantageOnAttack:!0,disadvantageOnAbilityChecks:!0}},deafen:{name:"Deafened",requiresConcentration:!1},burn:{name:"Burning",requiresConcentration:!1}};class V{constructor(e){this.diceRoller=e}castSpell(e,t,a){const i=t.level??0;if(!this.hasSpellSlot(e,i))return{success:!1,spellName:t.name,caster:e,targets:a,effectsApplied:[],spellSlotUsed:i,description:`${e.character.name} tried to cast ${t.name} but has no spell slots available!`};this.consumeSpellSlot(e,i);const n=[];let r,o;const l=t.damage_dice??t.damage,c=t.damage_type??t.damageType,u=t.saving_throw??t.save;if(l&&c){if(t.attack_roll)r=this.diceRoller?this.diceRoller.calculateDamage(l,0,!1):U.calculateDamage(l,0,!1);else if(u){o=this.calculateSaveDC(e,u);for(const m of a){const v=u.toLowerCase()==="dexterity"&&m.statusEffects.some(E=>E.mechanicalEffects?.disadvantageOnDexSaves);this.makeSavingThrow(m,u,o,v)||(r=this.diceRoller?this.diceRoller.calculateDamage(l,0,!1):U.calculateDamage(l,0,!1),m.currentHP-=r.total,m.currentHP<0&&(m.currentHP=0,m.isDefeated=!0))}}}const d=t.tags??[],h=new Set;for(const m of d){const f=di[m];if(f){h.add(f.name);const v={name:f.name,description:`${f.name} by ${e.character.name}`,duration:1,source:e.id,hasConcentration:f.requiresConcentration,mechanicalEffects:f.mechanicalEffects};for(const w of a)n.push({...v})}}const p=[t.description,t.effect].filter(Boolean).join(" ").toLowerCase();if(!h.has("Charmed")&&p.includes("charm")){const m={name:"Charmed",description:`Charmed by ${e.character.name}`,duration:1,source:e.id,hasConcentration:!0};for(const f of a)n.push({...m})}if(!h.has("Frightened")&&p.includes("frighten")){const m={name:"Frightened",description:`Frightened of ${e.character.name}`,duration:1,source:e.id,hasConcentration:!0};for(const f of a)n.push({...m})}const g=`${e.character.name} casts ${t.name} (Level ${i}) at ${a.map(m=>m.character.name).join(", ")}`;return{success:!0,spellName:t.name,caster:e,targets:a,saveDC:o,damage:r,effectsApplied:n,spellSlotUsed:i,description:g}}hasSpellSlot(e,t){return t===0?!0:!e.spellSlots||!e.spellSlots[t]?!1:e.spellSlots[t]>0}consumeSpellSlot(e,t){t!==0&&(e.spellSlots||(e.spellSlots={}),e.spellSlots[t]||(e.spellSlots[t]=0),e.spellSlots[t]--)}restoreSpellSlots(e){e.spellSlots=sa(e.character.level)}calculateSaveDC(e,t){const a=t.toLowerCase(),i=e.character.ability_modifiers[a]||0,n=e.character.proficiency_bonus;return 8+i+n}makeSavingThrow(e,t,a,i=!1){const n=t.toLowerCase(),r=e.character.ability_modifiers[n]||0,c=e.character.saving_throws[n]?e.character.proficiency_bonus:0;let u;return i?u=(this.diceRoller?this.diceRoller.rollWithDisadvantage():U.rollWithDisadvantage()).result+r+c:u=this.diceRoller?this.diceRoller.rollSavingThrow(r,c):U.rollSavingThrow(r,c),u>=a}getSpellSlotInfo(e){if(!e.spellSlots)return"No spell slots available";const t=[];for(let a=1;a<=9;a++){const i=e.spellSlots[a]||0;i>0&&t.push(`Level ${a}: ${i} slots`)}return t.length>0?t.join(", "):"No spell slots available"}canUpcast(e,t,a){const i=t.level??0;return a<i?!1:this.hasSpellSlot(e,a)}upcastSpell(e,t,a,i){const n=t.level??0;if(i<n)return{success:!1,spellName:t.name,caster:e,targets:a,effectsApplied:[],spellSlotUsed:i,description:`Cannot cast ${t.name} with a lower-level slot`};const r=t.level;t.level=i;const o=this.castSpell(e,t,a);return t.level=r,o}static hasSpellTag(e,t){return Array.isArray(e.tags)&&e.tags.includes(t)}static getSpellTags(e){return Array.isArray(e.tags)?[...e.tags]:[]}static isDamageSpell(e){return V.hasSpellTag(e,"damage")||!!(e.damage_dice||e.damage)}static requiresConcentration(e){return e.concentration===!0}static isAOESpell(e){return V.hasSpellTag(e,"aoe")}static isMultiTargetSpell(e){return V.hasSpellTag(e,"multi-target")}static isBonusActionSpell(e){return V.hasSpellTag(e,"bonus-action")}static isAllySpell(e){return V.hasSpellTag(e,"ally")}static isSelfSpell(e){return V.hasSpellTag(e,"self")}static getStatusEffectTags(){return di}}class gs{constructor(e){this.config=e}getStyleForCombatant(e){return this.config.overrides?.has(e.id)?this.config.overrides.get(e.id):e.id.startsWith("player")?this.config.playerStyle:this.config.enemyStyle}getSide(e){return e.id.startsWith("player")?"player":"enemy"}getEnemies(e,t){const i=this.getSide(e)==="player"?"enemy":"player";return t.combatants.filter(n=>n.id.startsWith(i)&&!n.isDefeated)}getAllies(e,t){const i=this.getSide(e)==="player"?"player":"enemy";return t.combatants.filter(n=>n.id.startsWith(i)&&!n.isDefeated)}assessThreat(e,t){const a=e.character.hp.max,i=a>0?e.currentHP/a:0,n=this.getAllies(e,t),r=this.getEnemies(e,t),o=n.reduce((v,w)=>{if(w.id===e.id)return v;const E=w.character.hp.max,I=E>0?w.currentHP/E:0;return Math.min(v,I)},1),l=r.reduce((v,w)=>Math.min(v,w.currentHP),r.length>0?1/0:0),c=r.reduce((v,w)=>{const E=w.character.equipment?.weapons.filter(D=>D.equipped)||[];let I=5;for(const D of E){const x=Ze[D.name],N=D.damage_dice||(typeof D.damage!="string"?D.damage?.dice:void 0)||x?.damage?.dice||"1d6",$=this.averageDamageFromFormula(N);$>I&&(I=$)}return Math.max(v,I)},0),d=(e.character.equipment?.items||[]).some(v=>v.quantity>0&&!v.equipped&&v.tags?.some(w=>w==="healing"||w==="consumable")),h=e.spellSlots,p=h!==void 0&&Object.values(h).some(v=>v>0),g=(e.legendaryActionsRemaining??0)>0,m=(e.legendaryResistancesRemaining??0)>0,f=g||m;return{myHPPercent:i,myAC:e.character.armor_class,lowestAllyHPPercent:o,lowestEnemyHP:l,highestEnemyDamage:c,partySize:n.length,enemyCount:r.length,roundNumber:t.roundNumber,isLowHP:i<.25,isCriticalHP:i<.1,hasHealingItems:d,hasSpellSlots:p,hasRemainingLimitedAbilities:f}}decide(e,t){const a=this.getStyleForCombatant(e),i=this.assessThreat(e,t);if(this.getEnemies(e,t).length===0)return{action:"skip",reasoning:"No valid targets remaining"};const r=this.selectSpellAction(e,t,i,a);if(r)return r;const o=this.selectItemAction(e,i,a);if(o)return o;const l=this.selectDefensiveAction(e,t,i,a);return l||this.selectAttackAction(e,t,i,a)}selectSpellAction(e,t,a,i){const n=this.getAvailableSpells(e);if(n.length===0)return null;const r=n.map(d=>this.evaluateSpell(d,e,t,a)),o=r.filter(d=>d.expectedDamage>0&&!d.isHealing),l=r.filter(d=>d.isHealing),c=r.filter(d=>d.isControl&&!d.isHealing),u=r.filter(d=>d.isBuff&&!d.isHealing);if(l.length>0&&this.shouldHeal(e,a,i)){const d=this.pickHealingSpell(l,e,t,a,i);if(d){const h=this.pickHealingTarget(e,t,i);return{action:"castSpell",spellName:d.spell.name,target:h?.id,targetIds:d.isMultiTarget?this.getHealingTargets(e,t,i).map(p=>p.id):void 0,reasoning:`Cast ${d.spell.name} to heal ${h?.character.name??"self"} (${i} style)`}}}if(o.length>0&&this.shouldCastDamageSpell(o,a,i)){const d=this.pickDamageSpell(o,a,i),h=this.pickSpellTargets(d,e,t,i);if(h.length>0)return{action:"castSpell",spellName:d.spell.name,target:h.length===1?h[0].id:void 0,targetIds:h.length>1?h.map(p=>p.id):void 0,reasoning:`Cast ${d.spell.name} on ${h.map(p=>p.character.name).join(", ")} (${i} style, ~${d.expectedDamage.toFixed(1)} expected damage)`}}if(c.length>0&&i==="normal"&&this.shouldCastControlSpell(a)){const d=c[0],h=this.pickSpellTargets(d,e,t,i);if(h.length>0)return{action:"castSpell",spellName:d.spell.name,target:h[0].id,reasoning:`Cast ${d.spell.name} for crowd control (${i} style)`}}if(u.length>0&&i==="normal"&&!a.isLowHP&&!a.isCriticalHP){const d=u[0],h=this.pickBuffTarget(e,t);return{action:"castSpell",spellName:d.spell.name,target:h?.id,reasoning:`Cast ${d.spell.name} on ${h?.character.name??"self"} (buff, normal style)`}}return null}getAvailableSpells(e){const t=e.character.combat_spells;return!t||t.length===0?[]:t.filter(a=>{if(!a.level||a.level===0)return!0;const i=e.spellSlots;return i?this.hasSlotForLevel(i,a.level):!1})}hasSlotForLevel(e,t){if((e[t]??0)>0)return!0;for(let a=t+1;a<=9;a++)if((e[a]??0)>0)return!0;return!1}evaluateSpell(e,t,a,i){const n=!e.level||e.level===0,r=n?0:this.getBestAvailableSlot(t.spellSlots,e.level??0),o=e.damage_dice||e.damage;let l=o?this.averageDamageFromFormula(o):0;const c=V.isAOESpell(e),u=V.isMultiTargetSpell(e),d=V.isAllySpell(e)||V.isSelfSpell(e)||e.tags?.includes("healing"),h=V.hasSpellTag(e,"buff"),p=V.hasSpellTag(e,"control")||V.hasSpellTag(e,"debuff");if((c||u)&&l>0){const g=i.enemyCount;g>1&&(l*=Math.min(g,4))}return!n&&l>0&&(l*=1.5),{spell:e,expectedDamage:l,isHealing:!!d,isBuff:!!h,isControl:!!p,isAOE:c,isMultiTarget:u,isAllySpell:V.isAllySpell(e),isSelfSpell:V.isSelfSpell(e),isBonusAction:V.isBonusActionSpell(e),requiresConcentration:V.requiresConcentration(e),slotLevel:r,isCantrip:n}}getBestAvailableSlot(e,t){if(!e)return 0;if((e[t]??0)>0)return t;for(let a=t+1;a<=9;a++)if((e[a]??0)>0)return a;return 0}shouldHeal(e,t,a){return a==="aggressive"?t.myHPPercent<.75:t.isLowHP||t.isCriticalHP}pickHealingSpell(e,t,a,i,n){if(e.length===0)return null;const r=[...e].sort((o,l)=>l.expectedDamage-o.expectedDamage);if(n==="aggressive")return r[0];if(!i.isCriticalHP){const o=r.find(l=>l.isCantrip);if(o)return o}return r[0]}pickHealingTarget(e,t,a){const i=this.getAllies(e,t);if(i.length===0)return null;const n=[...i].sort((r,o)=>{const l=r.currentHP/r.character.hp.max,c=o.currentHP/o.character.hp.max;return l-c});return a==="normal"&&n[0].id!==e.id?n[0].currentHP/n[0].character.hp.max<.5?n[0]:e:n[0]}getHealingTargets(e,t,a){const i=this.getAllies(e,t),n=a==="aggressive"?1:.75;return i.filter(r=>r.currentHP/r.character.hp.max<n)}shouldCastDamageSpell(e,t,a){if(e.length===0)return!1;if(a==="aggressive")return!0;const i=e[0];return!!(e.some(r=>r.isAOE||r.isMultiTarget)&&t.enemyCount>=3||i.isCantrip||i.expectedDamage>15&&!i.isCantrip)}pickDamageSpell(e,t,a){const i=[...e].sort((o,l)=>l.expectedDamage-o.expectedDamage);if(a==="aggressive")return i[0];const n=i.filter(o=>o.isCantrip),r=i.filter(o=>!o.isCantrip);if(n.length>0){const o=n[0];return r.length>0&&r[0].expectedDamage>o.expectedDamage*1.5&&(t.roundNumber>1||this.totalSlotsRemaining(t)>3)?r[0]:o}return i[0]}totalSlotsRemaining(e){return e.hasSpellSlots?5:0}pickSpellTargets(e,t,a,i){const n=this.getEnemies(t,a),r=this.getAllies(t,a);return e.isAllySpell||e.isHealing?r.length>0?[this.pickHealingTarget(t,a,i)??r[0]]:[]:e.isSelfSpell?[t]:e.isMultiTarget||e.isAOE?n:[this.selectTarget(n,i)]}shouldCastControlSpell(e){return e.enemyCount>=2}pickBuffTarget(e,t){return this.getAllies(e,t).reduce((i,n)=>{const r=Math.max(i.character.ability_scores.STR,i.character.ability_scores.DEX);return Math.max(n.character.ability_scores.STR,n.character.ability_scores.DEX)>r?n:i})}selectItemAction(e,t,a){if(!t.hasHealingItems)return null;const n=(e.character.equipment?.items||[]).filter(r=>r.quantity>0&&!r.equipped);if(n.length===0)return null;if(a==="aggressive"){if(t.myHPPercent<.75&&!t.hasSpellSlots)return{action:"useItem",itemName:n[0].name,target:e.id,reasoning:`Use ${n[0].name} to heal (aggressive: maintain HP for damage output)`}}else if(t.isLowHP&&!t.hasSpellSlots)return{action:"useItem",itemName:n[0].name,target:e.id,reasoning:`Use ${n[0].name} to heal (no spell slots available)`};return null}selectDefensiveAction(e,t,a,i){return i==="aggressive"?null:a.isLowHP&&a.partySize<=1&&a.enemyCount>1?{action:"dodge",reasoning:"Dodge — isolated and low HP, improving survivability"}:null}selectAttackAction(e,t,a,i){const n=this.getEnemies(e,t),r=this.selectTarget(n,i),o=this.selectWeapon(e,i);return{action:"attack",target:r.id,weaponName:o.name,reasoning:`Attack ${r.character.name} with ${o.name} (${i} style, expected ~${o.expectedDamage.toFixed(1)} damage)`}}selectTarget(e,t){if(e.length===0)throw new Error("No valid targets to select from");return e.length===1?e[0]:t==="aggressive"?e.reduce((a,i)=>i.currentHP<a.currentHP?i:a):e.reduce((a,i)=>i.character.armor_class<a.character.armor_class?i:a)}selectWeapon(e,t){const a=this.evaluateWeapons(e);return a.length===0?{name:"Unarmed Strike",expectedDamage:1,attackBonus:0,type:"melee",properties:[]}:a.length===1?a[0]:t==="aggressive"?a.reduce((i,n)=>n.expectedDamage>i.expectedDamage?n:i):a.reduce((i,n)=>{const r=i.expectedDamage+i.attackBonus*.1;return n.expectedDamage+n.attackBonus*.1>r?n:i})}evaluateWeapons(e){const t=[],a=e.character.equipment?.weapons.filter(n=>n.equipped)||[];for(const n of a){const r=Ze[n.name],o=n.damage_dice||(typeof n.damage!="string"?n.damage?.dice:void 0)||r?.damage?.dice||"1d6",l=this.averageDamageFromFormula(o),c=n.weaponProperties?.includes("ranged")||r?.weaponProperties?.includes("ranged")||!1,u=c?"DEX":"STR",h=Math.floor((e.character.ability_scores[u]-10)/2)+e.character.proficiency_bonus;t.push({name:n.name,expectedDamage:l,attackBonus:h,type:c?"ranged":"melee",properties:n.weaponProperties||r?.weaponProperties||[]})}const i=Math.floor((e.character.ability_scores.STR-10)/2);return t.push({name:"Unarmed Strike",expectedDamage:1+Math.max(0,i),attackBonus:i+e.character.proficiency_bonus,type:"melee",properties:[]}),t}selectLegendaryAction(e,t){const a=e.character.legendary_config;if(!a)return null;const i=e.legendaryActionsRemaining??0;if(i<=0)return null;const n=this.getStyleForCombatant(e),r=a.actions.filter(p=>p.cost<=i);if(r.length===0)return null;const o=this.getEnemies(e,t);if(o.length===0)return null;const l=r.filter(p=>p.damage||p.tags?.includes("damage")),c=r.filter(p=>p.tags?.includes("heal")),u=r.filter(p=>p.tags?.includes("control")),d=this.assessThreat(e,t);if(n==="aggressive"&&l.length>0){const g=[...l].sort((f,v)=>v.cost-f.cost)[0],m=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:g.id,target:m.id,reasoning:`Legendary action: ${g.name} (cost: ${g.cost}, aggressive: max damage)`}}if(l.length>0){const g=[...l].sort((f,v)=>f.cost-v.cost)[0],m=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:g.id,target:m.id,reasoning:`Legendary action: ${g.name} (cost: ${g.cost}, normal: conserve points)`}}if(c.length>0&&d.isLowHP)return{action:"legendaryAction",legendaryActionId:c[0].id,reasoning:`Legendary action: ${c[0].name} (healing, low HP)`};if(u.length>0){const p=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:u[0].id,target:p.id,reasoning:`Legendary action: ${u[0].name} (control)`}}const h=this.selectTarget(o,n);return{action:"legendaryAction",legendaryActionId:r[0].id,target:h.id,reasoning:`Legendary action: ${r[0].name} (fallback)`}}averageDamageFromFormula(e){if(!e)return 0;const a=e.replace(/\s/g,"").match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!a)return 0;const i=parseInt(a[1],10),n=parseInt(a[2],10),r=a[3]?parseInt(a[3],10):0,o=(n+1)/2;return i*o+r}isSupportArchetype(e){const t=e.character.combat_spells;if(!t||t.length===0)return!1;const a=t.some(n=>V.isAllySpell(n)||n.tags?.includes("healing")),i=t.some(n=>V.hasSpellTag(n,"buff"));return a||i}}class fs{constructor(e){this.diceRoller=e}rollInitiativeForCombatant(e){const t=e.character.ability_modifiers.dexterity??0,a=this.diceRoller?this.diceRoller.rollD20():U.rollD20(),i=a+t;return e.initiative=i,{combatant:e,d20Roll:a,dexModifier:t,initiativeTotal:i}}rollInitiativeForAll(e){const t=[];for(const i of e){const n=this.rollInitiativeForCombatant(i);t.push(n)}const a=[...e].sort((i,n)=>{if(i.initiative!==n.initiative)return n.initiative-i.initiative;const r=i.character.ability_modifiers.dexterity??0;return(n.character.ability_modifiers.dexterity??0)-r});return{results:t,sortedCombatants:a}}getNextCombatant(e,t){const a=(t+1)%e.length,i=a===0;return{combatant:e[a],index:a,isNewRound:i}}getInitiativeOrder(e){return e.map((t,a)=>{const i=t.character.ability_modifiers.dexterity??0;return`${a+1}. ${t.character.name} (Initiative: ${t.initiative}, DEX: ${i})`})}rerollInitiativeForCombatant(e){const t=e.character.ability_modifiers.dexterity??0,a=this.diceRoller?this.diceRoller.rollD20():U.rollD20();return e.initiative=a+t,e.initiative}delayTurn(e,t){const a=e.findIndex(r=>r.id===t);if(a===-1)return e;const i=[...e],[n]=i.splice(a,1);return i.splice(a+1,0,n),i}resortByInitiative(e){return[...e].sort((t,a)=>{if(t.initiative!==a.initiative)return a.initiative-t.initiative;const i=t.character.ability_modifiers.dexterity??0;return(a.character.ability_modifiers.dexterity??0)-i})}}class ue{constructor(e,t="scaled"){this.diceRoller=e,this.hitMode=t}static computeScaledDamage(e,t,a,i,n){const r=Math.max(1,Math.floor(e*2+(t-a)*.3)),o=ue.getWeaponBonus(i);return n?Math.max(1,Math.floor(r*1.5))+o:Math.max(1,r+o)}static computeDamageScale(e,t){if(e>=t)return 1;const a=t-e;return Math.max(.1,1-a*.1)}static computeAttackBonus(e,t,a){const i=t.includes("ranged"),n=t.includes("finesse");return Math.floor(((e[i||n?"DEX":"STR"]??10)-10)/2)+a}resolveAttack(e,t,a){const i=this.rollAttack(e,t,a);let n,r,o="";if(i.isMiss)o=`${e.character.name} uses ${a.name} against ${t.character.name} - CRITICAL MISS (natural 1)!`;else if(!i.hit)o=`${e.character.name} uses ${a.name} against ${t.character.name} - Miss (rolled ${i.d20Roll}, needed ${i.targetAC})`;else{if(n=this.rollDamage(e,t,a,i.isCritical),i.damageScale!==void 0&&i.damageScale<1){const l=Math.max(1,Math.floor(n.total*i.damageScale));n={...n,total:l}}r=t.currentHP-n.total,r<0?(r=0,t.currentHP=0,t.isDefeated=!0):t.currentHP=r,i.isCritical?o=`${e.character.name} uses ${a.name} against ${t.character.name} - CRITICAL HIT! (natural 20, doubled damage: ${n.total})`:o=`${e.character.name} uses ${a.name} against ${t.character.name} - Hit! (rolled ${i.d20Roll}, damage: ${n.total})`}return{attacker:e,target:t,attack:a,attackRoll:i,damageRoll:n,hpAfterDamage:r,description:o}}rollAttack(e,t,a){const i=this.diceRoller?this.diceRoller.rollD20():U.rollD20(),n=a.attack_bonus??0,r=i+n,o=t.character.armor_class,l=this.diceRoller?this.diceRoller.isCriticalHit(i):U.isCriticalHit(i),c=this.diceRoller?this.diceRoller.isCriticalMiss(i):U.isCriticalMiss(i);let u,d;return this.hitMode==="scaled"?(u=!c,!c&&!l?d=ue.computeDamageScale(r,o):d=1):u=!c&&(l||r>=o),{d20Roll:i,attackBonus:n,totalRoll:r,targetAC:o,hit:u,isCritical:l,isMiss:c,damageScale:d}}rollDamage(e,t,a,i){return this.hitMode==="dnd"?this.rollDamageDND(e,t,a,i):this.rollDamageScaled(e,t,a,i)}getDamageModifier(e,t){const a=e.character.ability_modifiers,i=a.STR??0,n=a.DEX??0;return t.type==="spell"?0:t.type==="ranged"?n:t.properties?.includes("finesse")?Math.max(i,n):i}rollDamageDND(e,t,a,i){const n=e.character.ability_scores.STR??10,r=e.character.ability_scores.DEX??10,o=a.properties?.includes("finesse")??!1,l=a.properties?.includes("ranged")??!1,c=Math.floor((n-10)/2),u=Math.floor((r-10)/2),d=o?Math.max(c,u):l?u:c,h=a.damage_dice??"",p=this.diceRoller?this.diceRoller.calculateDamage(h,d,i):U.calculateDamage(h,d,i),g=Math.max(1,p.total);return{diceFormula:h,rolls:p.rolls,modifier:d,total:g,isCritical:p.isCritical,baseDamage:0,weaponRoll:p.total-d}}rollDamageScaled(e,t,a,i){const n=e.character.level??1,r=e.character.ability_scores.STR??10,o=t.character.armor_class,l=Math.max(1,Math.floor(n*2+(r-o)*.3)),c=ue.getWeaponBonus(a.damage_dice),u=ue.computeScaledDamage(n,r,o,a.damage_dice??"",i);return{diceFormula:a.damage_dice??"",rolls:[],modifier:0,total:u,isCritical:i,baseDamage:l,weaponRoll:c}}static getWeaponBonus(e){if(!e)return 0;try{const t=U.parseDiceFormula(e),a=t.diceCount*(t.diceSides+1)/2;return Math.round(a/2.5)}catch{return 0}}static formatWeaponDamage(e,t,a){if(a==="scaled"){const i=ue.getWeaponBonus(e);return i>0?`+${i} ${t}`:t}return`${e} ${t}`}static estimateDamagePerHit(e){if(e.hitMode==="scaled")return ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!1);const t=Math.floor((e.abilityScore-10)/2);try{const a=U.parseDiceFormula(e.damageDice);return a.diceCount*(a.diceSides+1)/2+a.modifier+t}catch{return t+1}}static estimateDPR(e){const t=e.attackCount??1;if(e.hitMode==="scaled"){const l=ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!1),c=ue.computeScaledDamage(e.level,e.abilityScore,e.targetAC,e.damageDice,!0),u=e.proficiencyBonus??Math.ceil(1+((e.level||1)-1)/4),d=e.attackBonus??Math.floor((e.abilityScore-10)/2)+u;let h=0;for(let p=1;p<=20;p++){if(p===1)continue;if(p===20){h+=c;continue}const g=p+d,m=ue.computeDamageScale(g,e.targetAC);h+=Math.max(1,Math.floor(l*m))}return h/20*t}const a=ue.estimateDamagePerHit(e),i=e.proficiencyBonus??Math.ceil(1+((e.level||1)-1)/4),n=Math.floor((e.abilityScore-10)/2),r=e.attackBonus??n+i,o=Math.max(.05,Math.min(.95,(21-(e.targetAC-r))/20));return a*o*t}isInRange(e,t,a){if(!e.position||!t.position)return!0;const i=Math.sqrt(Math.pow(e.position.x-t.position.x,2)+Math.pow(e.position.y-t.position.y,2));return a.range?i<=a.range:i<=5}calculateAttackBonus(e,t,a,i=!1){let n=a;return i&&(n+=e.proficiency_bonus),n}attackWithAdvantage(e,t,a){const i=this.diceRoller?this.diceRoller.rollWithAdvantage():U.rollWithAdvantage(),n=i.roll1,r=i.roll2,o=i.result,l=a.attack_bonus??0,c=o+l,u=t.character.armor_class,d=(this.diceRoller?this.diceRoller.isCriticalHit(n):U.isCriticalHit(n))||(this.diceRoller?this.diceRoller.isCriticalHit(r):U.isCriticalHit(r)),h=this.diceRoller?this.diceRoller.isCriticalMiss(o):U.isCriticalMiss(o);let p,g;if(this.hitMode==="scaled")if(p=!h,!h&&!d&&c<u){const f=u-c;g=Math.max(.1,1-f*.1)}else g=1;else p=!h&&(d||c>=u);const m=`${e.character.name} attacks with advantage (rolled ${n} and ${r}, using ${o})`;if(p&&!h){let f=this.rollDamage(e,t,a,d);if(g!==void 0&&g<1){const w=Math.max(1,Math.floor(f.total*g));f={...f,total:w}}const v=t.currentHP-f.total;return v<0?(t.currentHP=0,t.isDefeated=!0):t.currentHP=v,{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},damageRoll:f,hpAfterDamage:t.currentHP,description:m}}return{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},description:m}}attackWithDisadvantage(e,t,a){const i=this.diceRoller?this.diceRoller.rollWithDisadvantage():U.rollWithDisadvantage(),n=i.roll1,r=i.roll2,o=i.result,l=a.attack_bonus??0,c=o+l,u=t.character.armor_class,d=this.diceRoller?this.diceRoller.isCriticalHit(o):U.isCriticalHit(o),h=(this.diceRoller?this.diceRoller.isCriticalMiss(n):U.isCriticalMiss(n))||(this.diceRoller?this.diceRoller.isCriticalMiss(r):U.isCriticalMiss(r));let p,g;if(this.hitMode==="scaled")if(p=!h,!h&&!d&&c<u){const f=u-c;g=Math.max(.1,1-f*.1)}else g=1;else p=!h&&(d||c>=u);const m=`${e.character.name} attacks with disadvantage (rolled ${n} and ${r}, using ${o})`;if(p&&!h){let f=this.rollDamage(e,t,a,d);if(g!==void 0&&g<1){const w=Math.max(1,Math.floor(f.total*g));f={...f,total:w}}const v=t.currentHP-f.total;return v<0?(t.currentHP=0,t.isDefeated=!0):t.currentHP=v,{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},damageRoll:f,hpAfterDamage:t.currentHP,description:m}}return{attacker:e,target:t,attack:a,attackRoll:{d20Roll:o,attackBonus:l,totalRoll:c,targetAC:u,hit:p,isCritical:d,isMiss:h,damageScale:g},description:m}}static simulateAttacks(e,t,a,i=1e3,n,r){const o=new ue(n,r),l=new Map;let c=0,u=0,d=0;for(let g=0;g<i;g++){const m={id:`sim-atk-${g}`,character:e,initiative:0,currentHP:e.hp.max,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1},f={id:`sim-tgt-${g}`,character:t,initiative:0,currentHP:t.hp.max,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1},v=o.resolveAttack(m,f,a),w=v.damageRoll?.total??0;l.set(w,(l.get(w)??0)+1),v.attackRoll.isMiss||!v.attackRoll.hit?d++:(c++,v.attackRoll.isCritical&&u++)}const h=Array.from(l.entries()).map(([g,m])=>({damage:g,count:m,percentage:m/i*100})).sort((g,m)=>g.damage-m.damage),p=h.reduce((g,m)=>g+m.damage*m.count,0)/i;return{iterations:i,totalHits:c,totalMisses:d,totalCrits:u,hitRate:c/i*100,critRate:u/i*100,missRate:d/i*100,averageDamage:p,maxDamage:Math.max(...l.keys()),distribution:h}}}class Se{constructor(e){this.seed=e,this.counter=0}random(){const e=y.deriveSeed(this.seed,this.counter.toString());return this.counter++,y.hashSeedToFloat(e)}randomInt(e,t){return Math.floor(this.random()*(t-e))+e}randomChoice(e){const t=this.randomInt(0,e.length);return e[t]}weightedChoice(e){const t=e.reduce((i,[,n])=>i+n,0);let a=this.random()*t;for(const[i,n]of e)if(a-=n,a<=0)return i;return e[e.length-1][0]}shuffle(e){const t=[...e];for(let a=t.length-1;a>0;a--){const i=this.randomInt(0,a+1);[t[a],t[i]]=[t[i],t[a]]}return t}reset(){this.counter=0}}const ys={1:{easy:25,medium:50,hard:75,deadly:100},2:{easy:50,medium:100,hard:150,deadly:200},3:{easy:75,medium:150,hard:225,deadly:300},4:{easy:125,medium:250,hard:375,deadly:500},5:{easy:250,medium:500,hard:750,deadly:1e3},6:{easy:300,medium:600,hard:900,deadly:1200},7:{easy:350,medium:750,hard:1100,deadly:1700},8:{easy:400,medium:800,hard:1200,deadly:1900},9:{easy:500,medium:1e3,hard:1500,deadly:2300},10:{easy:600,medium:1200,hard:1800,deadly:2400},11:{easy:800,medium:1600,hard:2400,deadly:3600},12:{easy:1e3,medium:2e3,hard:3e3,deadly:4500},13:{easy:1200,medium:2400,hard:3600,deadly:5400},14:{easy:1400,medium:2800,hard:4200,deadly:5700},15:{easy:1600,medium:3200,hard:4800,deadly:6400},16:{easy:2e3,medium:4e3,hard:6e3,deadly:8e3},17:{easy:2400,medium:4800,hard:7200,deadly:9600},18:{easy:3e3,medium:6e3,hard:9e3,deadly:12e3},19:{easy:4e3,medium:8e3,hard:12e3,deadly:16e3},20:{easy:5e3,medium:1e4,hard:15e3,deadly:2e4}},Mt={0:10,.125:25,.25:50,.5:100,1:200,2:450,3:700,4:1100,5:1800,6:2300,7:2900,8:3900,9:5e3,10:5900,11:7200,12:8400,13:1e4,14:11500,15:13e3,16:15e3,17:18e3,18:2e4,19:22e3,20:25e3,21:33e3,22:41e3,23:5e4,24:62e3,26:9e4,27:105e3,28:12e4,29:135e3,30:155e3},na={1:1,2:1.5,3:2,4:2,5:2,6:2,7:1.5,8:1.5,9:1.5,10:1.5,11:1,12:1,13:1,14:1,15:1},sr={DEFAULT:1,EASIER:.9,MUCH_EASIER:.8,HARDER:1.1,MUCH_HARDER:1.2,BRUTAL:1.5};function vs(s,e){const t=ys[s];return t&&t[e]||0}function Wt(s,e){return s.reduce((t,a)=>t+vs(a,e),0)}function ka(s){return Mt[s]||0}function Ta(s){if(s<=0)return 0;const e=Object.keys(Mt).map(Number).sort((n,r)=>n-r);let t=0,a=e.length-1,i=0;for(;t<=a;){const n=Math.floor((t+a)/2),r=e[n];Mt[r]<=s?(i=r,t=n+1):a=n-1}return i}function nr(s,e=1){return Math.round(s*e)}function ot(s){return s<=0?1:s>=15?na[15]:na[s]||1}function rr(s,e){const t=s.reduce((i,n)=>i+ka(n),0),a=e!==void 0?e:ot(s.length);return Math.round(t*a)}function bs(s){if(s.length===0)return 1;const e=s.reduce((t,a)=>t+a,0);return Math.floor(e/s.length)}function or(s){return typeof s=="string"&&["easy","medium","hard","deadly"].includes(s)}class ws{constructor(e={},t){this.diceRoller=t,this.initiativeRoller=new fs(t),this.attackResolver=new ue(t,e.hitMode),this.spellCaster=new V(t),this.config={useEnvironment:!0,useMusic:!1,tacticalMode:!1,maxTurnsBeforeDraw:100,allowFleeing:!1,seed:e.seed||`combat_${Date.now()}`,...e},this.rng=new Se(this.config.seed)}startCombat(e,t,a){const i=[];let n=0;for(const l of e)i.push(this.createCombatant(l,`player_${n++}`));for(const l of t)i.push(this.createCombatant(l,`enemy_${n++}`));const r=this.initiativeRoller.rollInitiativeForAll(i);return{id:`combat_${Date.now()}`,combatants:r.sortedCombatants,currentTurnIndex:0,roundNumber:1,environment:a,history:[],isActive:!0,startTime:Date.now(),lastUpdated:Date.now()}}getCurrentCombatant(e){return e.combatants[e.currentTurnIndex]}executeAttack(e,t,a,i){const n=this.getAttackAdvantageDisadvantage(t,a,i);let r;n==="advantage"?r=this.attackResolver.attackWithAdvantage(t,a,i):n==="disadvantage"?r=this.attackResolver.attackWithDisadvantage(t,a,i):r=this.attackResolver.resolveAttack(t,a,i);const o={type:"attack",actor:t,target:a,attack:i,result:{success:r.attackRoll.hit,roll:r.attackRoll.d20Roll,isCritical:r.attackRoll.isCritical,damage:r.damageRoll?.total,damageType:i.damage_type,targetHP:r.hpAfterDamage,description:r.description}};return e.history.push(o),a.currentHP<=0?(a.isDefeated=!0,a.concentratingOn&&(a.concentratingOn=void 0)):r.attackRoll.hit&&r.damageRoll?.total&&a.concentratingOn&&this.checkConcentration(e,a,r.damageRoll.total),o}executeWeaponAttack(e,t,a,i){const n=t.character.equipment?.weapons.filter(l=>l.equipped)||[];if(i==="unarmed"||n.length===0){const l=this.buildUnarmedAttack(t.character);return this.executeAttack(e,t,a,l)}const r=i?n.find(l=>l.name===i):n[0];if(!r)throw new Error(`Weapon "${i}" is not equipped`);const o=this.buildAttackFromWeapon(r,t.character);return this.executeAttack(e,t,a,o)}buildAttackFromWeapon(e,t){const a=Ze[e.name];if(!a)throw new Error(`Weapon "${e.name}" not found in equipment database`);const i=a.weaponProperties||[],n=i.includes("ranged"),r=t.proficiency_bonus,o=ue.computeAttackBonus(t.ability_scores,i,r);return{name:e.name,damage_dice:e.damage_dice||a.damage?.dice||"1d6",damage_type:a.damage?.damageType||"bludgeoning",type:n?"ranged":"melee",attack_bonus:o,properties:i}}buildUnarmedAttack(e){const t=Math.floor((e.ability_scores.STR-10)/2),a=e.proficiency_bonus;return{name:"Unarmed Strike",damage_dice:"1d1",damage_type:"bludgeoning",type:"melee",attack_bonus:t+a,properties:[]}}executeCastSpell(e,t,a,i){const n=this.spellCaster.castSpell(t,a,i),r={type:"spell",actor:t,targets:i,spell:a,result:{success:n.success,damage:n.damage?.total,damageType:a.damage_type??a.damageType,description:n.description}};if(e.history.push(r),n.success)for(const o of i)for(const l of n.effectsApplied)this.applyStatusEffect(o,l);for(const o of i)o.currentHP<=0?(o.isDefeated=!0,o.concentratingOn&&(o.concentratingOn=void 0)):n.success&&n.damage?.total&&o.concentratingOn&&this.checkConcentration(e,o,n.damage.total);return r}executeLegendaryAction(e,t,a,i){const n=t.character.legendary_config;if(!n)throw new Error(`${t.character.name} has no legendary actions (not a boss)`);const r=n.actions.find(p=>p.id===a.id);if(!r)throw new Error(`Legendary action "${a.id}" not found on ${t.character.name}`);const o=r.cost,l=t.legendaryActionsRemaining??0;if(l<o)throw new Error(`${t.character.name} needs ${o} legendary action points but only has ${l} remaining`);t.legendaryActionsRemaining=l-o;let c,u;if(r.damage&&i){const p=r.damage.replace(/\s/g,""),g=this.diceRoller;g?c=g.parseDiceFormula(p).total:c=U.parseDiceFormula(p).total;const m=this.applyDamage(i,c);u=i.currentHP,i.currentHP<=0?(i.isDefeated=!0,i.concentratingOn&&(i.concentratingOn=void 0)):i.concentratingOn&&m>0&&this.checkConcentration(e,i,m)}const d=i?`${t.character.name} uses ${r.name} on ${i.character.name}${c?` for ${c} ${r.damage_type??""} damage`:""} (${o} action point${o!==1?"s":""} spent, ${t.legendaryActionsRemaining} remaining)`:`${t.character.name} uses ${r.name} (${o} action point${o!==1?"s":""} spent, ${t.legendaryActionsRemaining} remaining)`,h={type:"legendaryAction",actor:t,target:i,legendaryAction:{id:r.id,name:r.name,description:r.effect,cost:r.cost,effect:r.effect,damage:r.damage,damageType:r.damage_type,archetypes:[]},result:{success:!0,damage:c,damageType:r.damage_type,targetHP:u,description:d}};return e.history.push(h),i?.isDefeated&&this.checkCombatStatus(e),h}useLegendaryResistance(e,t){const a=t.legendaryResistancesRemaining??0;return a<=0?!1:(t.legendaryResistancesRemaining=a-1,e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name} used legendary resistance (${t.legendaryResistancesRemaining} remaining today)`}}),!0)}executeDodge(e,t){const a={type:"dodge",actor:t,result:{success:!0,description:`${t.character.name} takes the Dodge action (AC increased until next turn)`}};return e.history.push(a),a}executeDash(e,t){const a={type:"dash",actor:t,result:{success:!0,description:`${t.character.name} takes the Dash action (double movement)`}};return e.history.push(a),a}executeDisengage(e,t){const a={type:"disengage",actor:t,result:{success:!0,description:`${t.character.name} takes the Disengage action (no opportunity attacks provoked)`}};return e.history.push(a),a}canFlee(){return this.config.allowFleeing===!0}executeFlee(e,t){if(!this.canFlee())throw new Error("Fleeing is not allowed in this combat. Enable with allowFleeing: true in CombatConfig.");const a={type:"flee",actor:t,result:{success:!0,description:`${t.character.name} flees from combat!`}};e.history.push(a);const i=e.combatants.findIndex(n=>n.id===t.id);return i!==-1&&(e.combatants.splice(i,1),i<e.currentTurnIndex?e.currentTurnIndex--:i===e.currentTurnIndex&&(e.currentTurnIndex=Math.min(e.currentTurnIndex,e.combatants.length-1))),t.isDefeated=!0,e.lastUpdated=Date.now(),this.checkCombatStatus(e),a}nextTurn(e){const t=e.combatants[e.currentTurnIndex];t.actionUsed=!1,t.bonusActionUsed=!1,t.reactionUsed=!1;const a=(this.config.maxTurnsBeforeDraw||100)+e.combatants.length;for(let i=0;i<a;i++){const n=(e.currentTurnIndex+1)%e.combatants.length;if(n===0){e.roundNumber++;for(const l of e.combatants)l.character.legendary_config&&!l.isDefeated&&(l.legendaryActionsRemaining=3)}e.currentTurnIndex=n,e.lastUpdated=Date.now();const o=e.combatants[n];if(!o.isDefeated){if(this.processStartOfTurnDamage(e,o),o.isDefeated){if(this.checkCombatStatus(e),!e.isActive)return e;continue}if(this.shouldSkipTurn(o)){if(o.actionUsed=!0,o.bonusActionUsed=!0,o.reactionUsed=!0,o.concentratingOn){const c=this.dropConcentration(o,"Incapacitated");c&&e.history.push({type:"statusEffectTick",actor:o,result:{success:!0,description:`${o.character.name} lost concentration on ${c.name} (incapacitated)`}})}const l=o.statusEffects.filter(c=>c.mechanicalEffects?.skipTurn).map(c=>c.name);if(e.history.push({type:"statusEffectTick",actor:o,result:{success:!0,description:`${o.character.name}'s turn is skipped (${l.join(", ")})`}}),this.tickStatusEffects(e,o),this.checkCombatStatus(e),!e.isActive)return e;continue}this.tickStatusEffects(e,o);break}}return this.checkCombatStatus(e),e}processStartOfTurnDamage(e,t){for(const a of t.statusEffects)if(a.damage&&a.damage>0){const i=this.applyDamage(t,a.damage);e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,damage:i,damageType:a.damageType,description:`${t.character.name} takes ${i} ${a.damageType??""} damage from ${a.name}`}}),t.concentratingOn&&(t.isDefeated?t.concentratingOn=void 0:this.checkConcentration(e,t,i))}}tickStatusEffects(e,t){if(t.statusEffects.length===0)return;for(const i of t.statusEffects)i.duration--;const a=this.removeExpiredStatusEffects(t);if(a.length>0){const i=a.map(n=>n.name).join(", ");e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name}: ${i} expired`}})}}getAttackAdvantageDisadvantage(e,t,a){let i=!1,n=!1;const r=a.type??"melee";for(const o of e.statusEffects)o.mechanicalEffects&&(o.mechanicalEffects.disadvantageOnAttack&&(n=!0),o.mechanicalEffects.disadvantageOnAttackNonSource&&o.source!==t.id&&(n=!0));for(const o of t.statusEffects)o.mechanicalEffects&&(o.mechanicalEffects.advantageOnMeleeAttackAgainst&&r==="melee"&&(i=!0),o.mechanicalEffects.advantageOnRangedAttackAgainst&&r==="ranged"&&(i=!0));return i&&n?"normal":i?"advantage":n?"disadvantage":"normal"}shouldSkipTurn(e){return e.statusEffects.some(t=>t.mechanicalEffects?.skipTurn)}checkCombatStatus(e){const t=e.combatants.filter(o=>o.id.startsWith("player")),a=e.combatants.filter(o=>o.id.startsWith("enemy")),i=a.every(o=>o.isDefeated),n=t.every(o=>o.isDefeated),r=e.history.length>=(this.config.maxTurnsBeforeDraw||100);(i||n||r)&&(e.isActive=!1,i&&!n?(e.winner=t.find(o=>!o.isDefeated),e.winnerSide="player"):n&&!i?(e.winner=a.find(o=>!o.isDefeated),e.winnerSide="enemy"):e.winnerSide="draw")}getCombatResult(e){if(e.isActive)return null;const t=e.combatants.filter(c=>c.id.startsWith("enemy")),a=e.combatants.filter(c=>c.isDefeated);let i=0;i=Math.ceil(e.history.length/Math.max(1,e.combatants.length));const n=t.filter(c=>c.isDefeated).reduce((c,u)=>{const d=u.character.cr??u.character.level;return c+ka(d)},0),r=e.winner?`${e.winner.character.name} won the combat!`:"Combat ended in a draw.";let o=0,l=[];if(this.config.treasure){if(typeof this.config.treasure.gold=="number")o=this.config.treasure.gold;else if(typeof this.config.treasure.gold=="object"&&this.config.treasure.gold!==null){const{min:c,max:u}=this.config.treasure.gold;o=c+Math.floor(this.rng.random()*(u-c+1))}l=this.config.treasure.items||[]}else o=Math.floor(this.rng.random()*100);return{winner:e.winner,winnerSide:e.winnerSide??"draw",defeated:a,roundsElapsed:e.roundNumber,totalTurns:i,xpAwarded:n,treasureAwarded:{gold:o,items:l},description:r}}createCombatant(e,t){const a={id:t,character:e,initiative:0,currentHP:e.hp.max,temporaryHP:0,statusEffects:[],isDefeated:!1,actionUsed:!1,bonusActionUsed:!1,reactionUsed:!1,spellSlots:this.initializeSpellSlots(e)};return e.legendary_config&&(a.legendaryActionsRemaining=3,a.legendaryResistancesRemaining=e.legendary_config.resistances_per_day),a}validateSpellSlots(e){const t=[],a=e.spells?.spell_slots;if(!a||Object.keys(a).length===0)return t;for(const[i,n]of Object.entries(a)){const r=Number(i);if(!Number.isInteger(r)||r<1||r>9){t.push({message:`Invalid spell level key "${i}" — must be an integer 1-9`,level:Number.isNaN(r)?void 0:r,severity:"error"});continue}if(typeof n!="object"||n===null){t.push({message:`Spell level ${r} has invalid slot data (expected { total, used }, got ${typeof n})`,level:r,severity:"error"});continue}const{total:o,used:l}=n;if(typeof o!="number"||typeof l!="number"){t.push({message:`Spell level ${r} has non-numeric total (${typeof o}) or used (${typeof l})`,level:r,severity:"error"});continue}o<0&&t.push({message:`Spell level ${r} has negative total (${o})`,level:r,severity:"error"}),l<0&&t.push({message:`Spell level ${r} has negative used (${l})`,level:r,severity:"error"}),o===0&&l!==0&&t.push({message:`Spell level ${r} has total=0 but used=${l} — no slots exist to consume`,level:r,severity:"warn"}),l>o&&t.push({message:`Spell level ${r} has used (${l}) exceeding total (${o})`,level:r,severity:"error"})}return t}validateCombatantSpellSlots(e){const t=[],a=e.character,i=a.spells?.spell_slots,n=e.spellSlots;if(!i||Object.keys(i).length===0){if(n&&Object.keys(n).length>0){const r=sa(a.level),o=Object.keys(r).sort(),l=Object.keys(n).sort();if(o.join(",")!==l.join(","))t.push({message:`Combatant has spell slots ${l.join(", ")} but fallback table for ${a.class} level ${a.level} expects ${o.join(", ")}`,severity:"warn"});else for(const c of o){const u=Number(c);n[u]!==r[u]&&t.push({message:`Spell level ${u}: combatant has ${n[u]} but fallback table expects ${r[u]}`,level:u,severity:"warn"})}}return t}if(!n)return Object.values(i).every(o=>o.total-o.used<=0)||t.push({message:"Character has spell slot data but combatant has no spell slots — expected some remaining",severity:"error"}),t;for(const[r,o]of Object.entries(i)){const l=Number(r),c=Math.max(0,o.total-o.used);if(c===0)n[l]!==void 0&&n[l]>0&&t.push({message:`Spell level ${l}: combatant has ${n[l]} slots but source indicates all used (total=${o.total}, used=${o.used})`,level:l,severity:"error"});else{const u=n[l]??0;u!==c&&t.push({message:`Spell level ${l}: combatant has ${u} but source expects ${c} (total=${o.total}, used=${o.used})`,level:l,severity:"error"})}}for(const r of Object.keys(n)){const o=Number(r);o in i||t.push({message:`Combatant has spell level ${o} with ${n[o]} slots but no source data for that level`,level:o,severity:"warn"})}return t}initializeSpellSlots(e){const t=e.spells?.spell_slots;if(t&&Object.keys(t).length>0){const n=this.validateSpellSlots(e).filter(r=>r.severity==="error");if(n.length>0)console.warn(`[CombatEngine] Spell slot validation failed for ${e.name} (${e.class}): `+n.map(r=>r.message).join("; ")+". Falling back to class table.");else{const r={};for(const[o,l]of Object.entries(t)){const c=l.total-l.used;c>0&&(r[Number(o)]=c)}return Object.keys(r).length>0?r:void 0}}if(["Wizard","Cleric","Sorcerer","Bard","Druid","Warlock","Paladin","Ranger"].includes(e.class))return sa(e.level)}getCombatSummary(e){const t=this.getCurrentCombatant(e);return`Round ${e.roundNumber}, Turn: ${t.character.name} (${t.currentHP}/${t.character.hp.max} HP)`}applyDamage(e,t){const a=Math.max(0,t);if(e.temporaryHP){const i=Math.min(e.temporaryHP,a);e.temporaryHP-=i;const n=a-i;e.currentHP-=n}else e.currentHP-=a;return e.currentHP<=0&&(e.currentHP=0,e.isDefeated=!0),a}healCombatant(e,t){const a=Math.min(t,e.character.hp.max-e.currentHP);return e.currentHP+=a,a}applyTemporaryHP(e,t){e.temporaryHP=Math.max(e.temporaryHP||0,t)}applyStatusEffect(e,t){t.hasConcentration&&e.concentratingOn&&this.dropConcentration(e,"New concentration spell cast");const a=e.statusEffects.findIndex(i=>i.name===t.name);if(a!==-1){const i=e.statusEffects[a];return i.duration=Math.max(i.duration,t.duration),t.damage!==void 0&&(i.damage=Math.max(i.damage??0,t.damage)),t.source!==void 0&&(i.source=t.source),t.mechanicalEffects&&(i.mechanicalEffects={...i.mechanicalEffects,...t.mechanicalEffects}),t.hasConcentration&&(i.hasConcentration=!0),t.damageType&&(i.damageType=t.damageType),t.hasConcentration&&(e.concentratingOn=t.name),i}return e.statusEffects.push(t),t.hasConcentration&&(e.concentratingOn=t.name),t}dropConcentration(e,t="Concentration ended"){if(!e.concentratingOn)return;const a=e.concentratingOn,i=e.statusEffects.findIndex(n=>n.name===a);if(i!==-1){const[n]=e.statusEffects.splice(i,1);return e.concentratingOn=void 0,n}e.concentratingOn=void 0}rollConcentrationSave(e,t){if(!e.concentratingOn)return!0;const a=Math.max(10,Math.floor(t/2)),i=e.character.ability_modifiers.CON??0,r=e.character.saving_throws.CON??!1?e.character.proficiency_bonus:0,o=this.diceRoller?this.diceRoller.rollSavingThrow(i,r):U.rollSavingThrow(i,r),l=o-i-r;return l===1?!1:l===20?!0:o>=a}checkConcentration(e,t,a){if(!t.concentratingOn)return!1;if(!this.rollConcentrationSave(t,a)){const n=this.dropConcentration(t,"Took damage and failed concentration save");return n&&e.history.push({type:"statusEffectTick",actor:t,result:{success:!0,description:`${t.character.name} lost concentration on ${n.name} (took ${a} damage)`}}),!0}return!1}removeExpiredStatusEffects(e){const t=e.statusEffects.filter(a=>a.duration<=0);return e.statusEffects=e.statusEffects.filter(a=>a.duration>0),e.concentratingOn&&(e.statusEffects.some(i=>i.name===e.concentratingOn)||(e.concentratingOn=void 0)),t}getLivingCombatants(e){return e.combatants.filter(t=>!t.isDefeated)}getDefeatedCombatants(e){return e.combatants.filter(t=>t.isDefeated)}}class Ss{computeMetrics(e){const t=new Map;for(const o of e.combatants){const l=o.id.startsWith("player")?"player":"enemy";t.set(o.id,{combatantId:o.id,name:o.character.name,side:l,totalDamageDealt:0,totalDamageTaken:0,totalHealingDone:0,spellsCast:0,itemsUsed:0,criticalHits:0,hits:0,misses:0,kills:0,roundsSurvived:0,survived:!o.isDefeated,actionsByType:{},damagePerRound:[],hpRemainingPercent:0})}const a=new Set(["attack","spell","dodge","dash","disengage","flee","help","hide","ready","useItem","skip"]),i=new Map;for(const o of e.history){const l=o.actor.id,c=t.get(l);switch(c&&(c.actionsByType[o.type]=(c.actionsByType[o.type]||0)+1),a.has(o.type)&&i.set(l,(i.get(l)??0)+1),o.type){case"attack":{const u=o.result?.damage??0,d=o.result?.isCritical===!0,h=o.result?.success===!0;if(c&&(h?c.hits+=1:c.misses+=1),c&&u>0&&(c.totalDamageDealt+=u,c.criticalHits+=d?1:0),o.target){const p=t.get(o.target.id);p&&u>0&&(p.totalDamageTaken+=u)}break}case"spell":{const u=o.result?.damage??0,d=o.result?.isCritical===!0,h=this.isHealingSpell(o);if(c)if(c.spellsCast+=1,h){const p=o.targets??(o.target?[o.target]:[]);for(const g of p){const m=o.result?.damage??0;m>0&&(c.totalHealingDone+=m)}}else u>0&&(c.totalDamageDealt+=u,c.criticalHits+=d?1:0,o.result?.success===!0?c.hits+=1:c.misses+=1);if(!h&&u>0){const p=o.targets??(o.target?[o.target]:[]);for(const g of p){const m=t.get(g.id);m&&(m.totalDamageTaken+=u)}}break}case"legendaryAction":{const u=o.result?.damage??0;if(c&&u>0&&(c.totalDamageDealt+=u),o.target&&u>0){const d=t.get(o.target.id);d&&(d.totalDamageTaken+=u)}break}case"useItem":{c&&(c.itemsUsed+=1);break}}}const n=new Map;for(const o of e.history)if((o.type==="attack"||o.type==="spell"||o.type==="legendaryAction")&&o.target){const l=o.result?.damage??0;if(l>0&&n.set(o.target.id,o.actor.id),o.targets)for(const c of o.targets)l>0&&n.set(c.id,o.actor.id)}for(const o of e.combatants)if(o.isDefeated){const l=n.get(o.id);if(l){const c=t.get(l);c&&(c.kills+=1)}}const r=e.roundNumber;for(const[o,l]of t){l.roundsSurvived=Math.max(1,r);const c=i.get(o)??0;c>0&&l.totalDamageDealt>0&&(l.damagePerRound=[l.totalDamageDealt/c]);const u=e.combatants.find(d=>d.id===o);u&&u.character.hp.max>0&&(l.hpRemainingPercent=l.survived?Math.round(u.currentHP/u.character.hp.max*100):0)}return e.metrics=t,t}isHealingSpell(e){if(!e.spell)return!1;const t=e.spell.tags??[];return t.includes("healing")||t.includes("heal")}}class _s{runFullCombat(e,t,a,i,n){const r=new ws(i??{},n),o=new gs(a),l=r.startCombat(e,t);if(l.combatants.length===0)return{combat:l,result:{winnerSide:"draw",defeated:[],roundsElapsed:0,totalTurns:0,xpAwarded:0,description:"No combatants — combat could not start."},metrics:new Map};for(;l.isActive;){const h=r.getCurrentCombatant(l);if(h.isDefeated){r.nextTurn(l);continue}if(this.hasSkipTurnEffect(h)){l.history.push({type:"statusEffectTick",actor:h,result:{success:!0,description:`${h.character.name}'s turn is skipped (${h.statusEffects.filter(g=>g.mechanicalEffects?.skipTurn).map(g=>g.name).join(", ")})`}}),r.nextTurn(l);continue}const p=o.decide(h,l);if(this.executeDecision(r,l,h,p),!l.isActive||(this.processLegendaryActions(r,o,l,h),!l.isActive))break;r.nextTurn(l)}const c=r.getCombatResult(l);if(!c)throw new Error("AICombatRunner: combat ended but getCombatResult returned null");const d=new Ss().computeMetrics(l);return{combat:l,result:c,metrics:d}}executeDecision(e,t,a,i){switch(i.action){case"attack":{const n=this.findCombatant(t,i.target);n&&this.executeWeaponAttack(e,t,a,n,i.weaponName);break}case"castSpell":{const n=this.findSpell(a,i.spellName);if(n){const r=this.resolveTargets(t,i.target,i.targetIds);r.length>0&&e.executeCastSpell(t,a,n,r)}break}case"dodge":e.executeDodge(t,a);break;case"dash":e.executeDash(t,a);break;case"disengage":e.executeDisengage(t,a);break;case"flee":{e.canFlee()?e.executeFlee(t,a):this.fallbackAttack(e,t,a);break}case"useItem":{const n=a.character.equipment?.items||[],r=i.itemName?n.find(o=>o.name===i.itemName&&o.quantity>0):void 0;if(r&&(r.quantity-=1,r.quantity<=0)){const o=n.indexOf(r);o!==-1&&n.splice(o,1)}t.history.push({type:"useItem",actor:a,result:{success:!!r,description:r?`${a.character.name} uses ${i.itemName}`:`${a.character.name} tries to use ${i.itemName??"an item"} — item not found`}});break}case"legendaryAction":{this.executeLegendaryDecision(e,t,a,i);break}case"skip":t.history.push({type:"statusEffectTick",actor:a,result:{success:!0,description:`${a.character.name} skips their turn (${i.reasoning??"no action"})`}});break}}processLegendaryActions(e,t,a,i){for(const n of a.combatants)if(n.character.legendary_config&&!n.isDefeated&&n.id!==i.id){for(;a.isActive&&(n.legendaryActionsRemaining??0)>0;){const r=t.selectLegendaryAction(n,a);if(!r||(this.executeLegendaryDecision(e,a,n,r),!a.isActive))break}if(!a.isActive)break}}executeLegendaryDecision(e,t,a,i){const n=a.character.legendary_config;if(!n||!i.legendaryActionId)return;const r=n.actions.find(l=>l.id===i.legendaryActionId);if(!r)return;const o=i.target?this.findCombatant(t,i.target):void 0;try{e.executeLegendaryAction(t,a,{id:r.id,name:r.name,cost:r.cost,effect:r.effect,damage:r.damage,damage_type:r.damage_type},o)}catch{}}executeWeaponAttack(e,t,a,i,n){const r=n==="Unarmed Strike"?"unarmed":n;try{e.executeWeaponAttack(t,a,i,r)}catch{try{e.executeWeaponAttack(t,a,i,"unarmed")}catch{}}}findCombatant(e,t){if(t)return e.combatants.find(a=>a.id===t)}findSpell(e,t){if(t)return e.character.combat_spells?.find(a=>a.name===t)}resolveTargets(e,t,a){if(a&&a.length>0)return a.map(i=>e.combatants.find(n=>n.id===i)).filter(i=>i!==void 0);if(t){const i=e.combatants.find(n=>n.id===t);return i?[i]:[]}return[]}fallbackAttack(e,t,a){const i=a.id.startsWith("player")?"enemy":"player",n=t.combatants.filter(r=>r.id.startsWith(i)&&!r.isDefeated);n.length>0&&this.executeWeaponAttack(e,t,a,n[0],void 0)}hasSkipTurnEffect(e){return e.statusEffects.some(t=>t.mechanicalEffects?.skipTurn)}}function As(s){return new ks(s)}class ks{constructor(e){this.rng=typeof e=="string"?new Se(e):e}rollDie(e){if(e<1)throw new Error("Die must have at least 1 side");return this.rng.randomInt(1,e+1)}rollMultipleDice(e,t){const a=[];for(let i=0;i<e;i++)a.push(this.rollDie(t));return a}parseDiceFormula(e){const t=e.match(/^(\d+)d(\d+)([+-]\d+)?$/);if(!t)throw new Error(`Invalid dice formula: ${e}`);const a=parseInt(t[1],10),i=parseInt(t[2],10),n=t[3]?parseInt(t[3],10):0,r=this.rollMultipleDice(a,i),o=r.reduce((l,c)=>l+c,0)+n;return{diceCount:a,diceSides:i,modifier:n,rolls:r,total:o}}rollD20(){return this.rollDie(20)}rollWithAdvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.max(e,t)}}rollWithDisadvantage(){const e=this.rollD20(),t=this.rollD20();return{roll1:e,roll2:t,result:Math.min(e,t)}}rollInitiative(e){return this.rollD20()+e}isCriticalHit(e){return e===20}isCriticalMiss(e){return e===1}doubleDamage(e){return[...e,...e]}calculateDamage(e,t,a=!1){let n=this.parseDiceFormula(e).rolls;a&&(n=this.doubleDamage(n));const r=n.reduce((o,l)=>o+l,0)+t;return{diceFormula:e,rolls:n,modifier:t,total:r,isCritical:a}}rollSavingThrow(e,t=0){return this.rollD20()+e+t}rollAbilityCheck(e,t=0){return this.rollD20()+e+t}rollPercentile(){return this.rollDie(100)}}const lr=[{id:"skeleton",name:"Skeleton",category:"undead",archetype:"archer",signatureAbility:{id:"skeleton_bone_shot",name:"Bone Shot",description:"Fires a sharpened bone projectile that deals piercing damage. The hollow whistle of the bone strikes fear into the living.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["ammo","accuracy"]},baseStats:{STR:12,DEX:14,CON:12,INT:6,WIS:8,CHA:5},baseHP:13,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"zombie",name:"Zombie",category:"undead",archetype:"brute",signatureAbility:{id:"zombie_undead_grip",name:"Undead Grip",description:"A grapple followed by a bite that deals necrotic damage. The zombie's grip is supernaturally strong and unnaturally cold.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["grapple","persistent"]},baseStats:{STR:14,DEX:8,CON:14,INT:3,WIS:6,CHA:5},baseHP:22,baseAC:8,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"wight",name:"Wight",category:"undead",archetype:"brute",signatureAbility:{id:"wight_life_drain",name:"Life Drain",description:"A melee attack that deals necrotic damage and heals the wight for half the damage dealt. The stolen life force sustains its unholy existence.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["lifesteal"]},baseStats:{STR:16,DEX:12,CON:14,INT:10,WIS:12,CHA:14},baseHP:26,baseAC:14,baseSpeed:30,audioPreference:{bass:.3,mid:.6,treble:.2},resistances:{resistances:["necrotic"],immunities:["poison"]}},{id:"ghost",name:"Ghost",category:"undead",archetype:"support",signatureAbility:{id:"ghost_horrifying_visage",name:"Horrifying Visage",description:"Projects terrifying spectral visage that frightens nearby enemies. Frightened creatures have disadvantage on ability checks and attack rolls.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:30,properties:["debuff","fear","control"]},baseStats:{STR:8,DEX:14,CON:12,INT:12,WIS:14,CHA:16},baseHP:15,baseAC:11,baseSpeed:0,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["acid","cold","fire","lightning","thunder"],immunities:["poison","necrotic"]}}],cr=[{id:"imp",name:"Imp",category:"fiend",archetype:"archer",signatureAbility:{id:"imp_sting",name:"Sting",description:"A poisonous tail strike that deals poison damage and may poison the target. The imp's venom causes necrosis and weakness.",damageDie:"d6",damageType:"poison",attackType:"ranged",range:30,properties:["poison","debuff"]},baseStats:{STR:6,DEX:15,CON:10,INT:11,WIS:10,CHA:12},baseHP:10,baseAC:13,baseSpeed:20,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"quasit",name:"Quasit",category:"fiend",archetype:"support",signatureAbility:{id:"quasit_fear_aura",name:"Fear Aura",description:"Projects an aura of unnatural terror that frightens nearby enemies. Frightened creatures have disadvantage on ability checks.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:20,properties:["debuff","fear","control"]},baseStats:{STR:8,DEX:12,CON:10,INT:10,WIS:11,CHA:12},baseHP:7,baseAC:13,baseSpeed:40,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"lemure",name:"Lemure",category:"fiend",archetype:"brute",signatureAbility:{id:"lemure_hellish_resilience",name:"Hellish Resilience",description:"A slam attack that deals fire damage while simultaneously healing the lemure. Its corrupted flesh regenerates from hellfire.",damageDie:"d6",damageType:"fire",attackType:"melee",properties:["lifesteal","persistent"]},baseStats:{STR:10,DEX:6,CON:12,INT:3,WIS:8,CHA:5},baseHP:13,baseAC:7,baseSpeed:30,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{resistances:["fire","cold"],immunities:["poison"]}},{id:"demon",name:"Demon",category:"fiend",archetype:"brute",signatureAbility:{id:"demon_chaos_claw",name:"Chaos Claw",description:"A claw attack infused with chaotic energy that deals random damage types. Each strike channels the unpredictability of the abyss.",damageDie:"d6",damageType:"necrotic",attackType:"melee",properties:["chaotic","versatile"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:10},baseHP:28,baseAC:13,baseSpeed:30,audioPreference:{bass:.8,mid:.3,treble:.2},resistances:{resistances:["fire","cold"],immunities:["poison"]}}],ur=[{id:"fire-elemental",name:"Fire Elemental",category:"elemental",archetype:"brute",signatureAbility:{id:"fire_elemental_burning_touch",name:"Burning Touch",description:"A searing touch that deals fire damage and applies burning damage over time. The flames continue to burn after impact.",damageDie:"d6",damageType:"fire",attackType:"melee",properties:["ongoing","burn"]},baseStats:{STR:14,DEX:12,CON:16,INT:6,WIS:10,CHA:6},baseHP:26,baseAC:13,baseSpeed:50,audioPreference:{bass:.8,mid:.2,treble:.1},resistances:{immunities:["fire"],resistances:[]}},{id:"earth-elemental",name:"Earth Elemental",category:"elemental",archetype:"brute",signatureAbility:{id:"earth_elemental_earth_slam",name:"Earth Slam",description:"A devastating slam that deals bludgeoning damage in an area and knocks targets prone. The ground cracks under its power.",damageDie:"d8",damageType:"bludgeoning",attackType:"melee",properties:["aoe","prone"]},baseStats:{STR:18,DEX:8,CON:16,INT:6,WIS:10,CHA:6},baseHP:30,baseAC:17,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{immunities:["poison"],resistances:["necrotic"]}},{id:"water-elemental",name:"Water Elemental",category:"elemental",archetype:"support",signatureAbility:{id:"water_elemental_whirlpool",name:"Whirlpool",description:"Creates a swirling vortex that restrains targets and pulls them toward the center. The crushing waves hold enemies in place.",damageDie:"d6",damageType:"cold",attackType:"spell",range:30,properties:["control","restrain","pull"]},baseStats:{STR:14,DEX:14,CON:14,INT:8,WIS:12,CHA:8},baseHP:22,baseAC:14,baseSpeed:30,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{immunities:["cold"],resistances:["fire","necrotic"]}},{id:"air-elemental",name:"Air Elemental",category:"elemental",archetype:"archer",signatureAbility:{id:"air_elemental_wind_blast",name:"Wind Blast",description:"Projects a concentrated gust of wind that pushes targets back and deals force damage. The howling wind disrupts formation.",damageDie:"d6",damageType:"force",attackType:"ranged",range:60,properties:["push","control"]},baseStats:{STR:12,DEX:16,CON:14,INT:8,WIS:12,CHA:8},baseHP:18,baseAC:15,baseSpeed:90,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{immunities:["lightning"],resistances:["thunder"]}}],dr=[{id:"animated-armor",name:"Animated Armor",category:"construct",archetype:"brute",signatureAbility:{id:"animated_armor_slam",name:"Slam",description:"A powerful melee attack that deals force damage. The enchanted armor strikes with supernatural strength.",damageDie:"d6",damageType:"force",attackType:"melee",properties:["force"]},baseStats:{STR:14,DEX:10,CON:14,INT:3,WIS:6,CHA:1},baseHP:18,baseAC:18,baseSpeed:25,audioPreference:{bass:.9,mid:.1,treble:.1},resistances:{immunities:["poison","psychic"]}},{id:"golem",name:"Golem",category:"construct",archetype:"brute",signatureAbility:{id:"golem_immutable_form",name:"Immutable Form",description:"A devastating slam that deals bludgeoning damage. The golem is immune to most status conditions and non-magical physical attacks.",damageDie:"d8",damageType:"bludgeoning",attackType:"melee",properties:["status_immunity","nonmagical_resistance"]},baseStats:{STR:18,DEX:8,CON:16,INT:3,WIS:6,CHA:1},baseHP:32,baseAC:15,baseSpeed:20,audioPreference:{bass:.9,mid:.1,treble:0},resistances:{immunities:["poison","psychic"],resistances:["bludgeoning","piercing","slashing"]}},{id:"flying-sword",name:"Flying Sword",category:"construct",archetype:"archer",signatureAbility:{id:"flying_sword_diving_strike",name:"Diving Strike",description:"A diving attack that deals bonus damage when moving toward the target. The enchanted sword strikes with supernatural precision.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["charge","accuracy"]},baseStats:{STR:12,DEX:16,CON:12,INT:1,WIS:4,CHA:1},baseHP:10,baseAC:17,baseSpeed:50,audioPreference:{bass:.1,mid:.1,treble:.9},resistances:{immunities:["poison","psychic"]}},{id:"shield-guardian",name:"Shield Guardian",category:"construct",archetype:"support",signatureAbility:{id:"shield_guardian_protection_aura",name:"Protection Aura",description:"Projects a protective field that increases armor class of nearby allies. The magical shield deflects attacks meant for others.",damageDie:"d6",damageType:"force",attackType:"spell",range:20,properties:["buff","ally","defense"]},baseStats:{STR:14,DEX:10,CON:16,INT:6,WIS:10,CHA:5},baseHP:22,baseAC:16,baseSpeed:30,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{immunities:["poison","psychic"]}}],hr=[{id:"young-red-dragon",name:"Young Red Dragon",category:"dragon",archetype:"brute",signatureAbility:{id:"young_red_dragon_fire_breath",name:"Fire Breath",description:"Exhales a cone of searing flame that deals fire damage to all creatures in the area. The intense heat can ignite flammable objects.",damageDie:"d8",damageType:"fire",attackType:"ranged",range:30,properties:["aoe","cone","burn"]},baseStats:{STR:19,DEX:12,CON:17,INT:12,WIS:13,CHA:15},baseHP:45,baseAC:18,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{immunities:["fire"],resistances:[]}},{id:"dragon-wyrmling",name:"Dragon Wyrmling",category:"dragon",archetype:"brute",signatureAbility:{id:"dragon_wyrmling_multiattack",name:"Bite + Claw",description:"A combination attack with bite and claws that deals multiple physical attacks. The wyrmling attacks with ferocious hunger.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack"]},baseStats:{STR:15,DEX:12,CON:14,INT:10,WIS:11,CHA:12},baseHP:26,baseAC:17,baseSpeed:30,audioPreference:{bass:.3,mid:.6,treble:.2},resistances:{immunities:[],resistances:["acid"]}},{id:"drake",name:"Drake",category:"dragon",archetype:"brute",signatureAbility:{id:"drake_tail_swipe",name:"Tail Swipe",description:"A powerful tail strike that deals bludgeoning damage and knocks targets back. The force can push enemies away from the drake.",damageDie:"d6",damageType:"bludgeoning",attackType:"melee",properties:["knockback","push"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:10},baseHP:32,baseAC:15,baseSpeed:40,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{immunities:[],resistances:["cold"]}},{id:"young-blue-dragon",name:"Young Blue Dragon",category:"dragon",archetype:"archer",signatureAbility:{id:"young_blue_dragon_lightning_breath",name:"Lightning Breath",description:"Exhales a line of crackling lightning that deals lightning damage to all creatures in its path. The electrical discharge arcs between targets.",damageDie:"d8",damageType:"lightning",attackType:"ranged",range:60,properties:["aoe","line","chain"]},baseStats:{STR:17,DEX:14,CON:15,INT:14,WIS:13,CHA:15},baseHP:38,baseAC:19,baseSpeed:40,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{immunities:["lightning"],resistances:["thunder"]}}],pr=[{id:"owlbear",name:"Owlbear",category:"monstrosity",archetype:"brute",signatureAbility:{id:"owlbear_multiattack",name:"Multiattack",description:"A devastating combination of beak and claws attacks. The owlbear unleashes its primal fury in a flurry of strikes.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack"]},baseStats:{STR:18,DEX:10,CON:14,INT:3,WIS:10,CHA:6},baseHP:37,baseAC:13,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{resistances:[]}},{id:"griffin",name:"Griffin",category:"monstrosity",archetype:"archer",signatureAbility:{id:"griffin_dive_attack",name:"Dive Attack",description:"A high-speed diving attack from above that deals extra piercing damage. The griffin uses its powerful wings to strike with devastating force.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["charge","flying"]},baseStats:{STR:16,DEX:15,CON:14,INT:4,WIS:12,CHA:7},baseHP:30,baseAC:12,baseSpeed:50,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}},{id:"mimic",name:"Mimic",category:"monstrosity",archetype:"brute",signatureAbility:{id:"mimic_adhesive",name:"Adhesive",description:"A pseudopod attack that deals bludgeoning damage and grapples the target. The mimic's adhesive surface traps prey.",damageDie:"d6",damageType:"bludgeoning",attackType:"melee",properties:["grapple","ambush"]},baseStats:{STR:14,DEX:10,CON:12,INT:6,WIS:8,CHA:4},baseHP:21,baseAC:12,baseSpeed:10,audioPreference:{bass:.2,mid:.7,treble:.3},resistances:{resistances:["acid"]}},{id:"basilisk",name:"Basilisk",category:"monstrosity",archetype:"support",signatureAbility:{id:"basilisk_petrifying_gaze",name:"Petrifying Gaze",description:"A gaze attack that can stun targets. Those who meet the basilisk's eyes feel their limbs turning to stone.",damageDie:"d6",damageType:"psychic",attackType:"spell",range:30,properties:["debuff","control","stun"]},baseStats:{STR:14,DEX:10,CON:12,INT:4,WIS:6,CHA:6},baseHP:26,baseAC:14,baseSpeed:20,audioPreference:{bass:.2,mid:.7,treble:.2},resistances:{resistances:["poison"]}}],mr=[{id:"orc",name:"Orc",category:"humanoid",archetype:"brute",signatureAbility:{id:"orc_savage_strike",name:"Savage Strike",description:"A powerful melee attack that deals extra damage. The ferocity of the attack embodies primal aggression.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["versatile"]},baseStats:{STR:16,DEX:12,CON:14,INT:8,WIS:10,CHA:8},baseHP:15,baseAC:13,baseSpeed:30,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{resistances:["poison"]}},{id:"bandit",name:"Bandit",category:"humanoid",archetype:"brute",signatureAbility:{id:"bandit_cheap_shot",name:"Cheap Shot",description:"A dirty fighting technique that deals bonus damage against surprised or flat-footed opponents.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["finesse"]},baseStats:{STR:13,DEX:14,CON:12,INT:10,WIS:10,CHA:11},baseHP:11,baseAC:12,baseSpeed:30,audioPreference:{bass:.4,mid:.6,treble:.3},resistances:{resistances:[]}},{id:"hunter",name:"Hunter",category:"humanoid",archetype:"archer",signatureAbility:{id:"hunter_precise_shot",name:"Precise Shot",description:"A carefully aimed shot that can ignore half cover. Exceptional accuracy allows targeting vulnerabilities.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:120,properties:["accuracy"]},baseStats:{STR:11,DEX:16,CON:12,INT:12,WIS:14,CHA:10},baseHP:10,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.3,treble:.8},resistances:{resistances:[]}},{id:"goblin-archer",name:"Goblin Archer",category:"humanoid",archetype:"archer",signatureAbility:{id:"goblin_sneaky_shot",name:"Sneaky Shot",description:"A quick shot from hiding that deals bonus damage. Works best when the goblin is unseen.",damageDie:"d6",damageType:"piercing",attackType:"ranged",range:80,properties:["finesse","stealth"]},baseStats:{STR:8,DEX:15,CON:10,INT:10,WIS:8,CHA:8},baseHP:7,baseAC:13,baseSpeed:30,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}},{id:"shaman",name:"Shaman",category:"humanoid",archetype:"support",signatureAbility:{id:"shaman_spirit_bond",name:"Spirit Bond",description:"Channels spiritual energy to grant nearby allies a damage bonus on their next attack.",damageDie:"d6",damageType:"force",attackType:"spell",range:30,properties:["buff","ally"]},baseStats:{STR:10,DEX:10,CON:12,INT:12,WIS:15,CHA:13},baseHP:9,baseAC:11,baseSpeed:30,audioPreference:{bass:.3,mid:.7,treble:.3},resistances:{resistances:["necrotic"]}},{id:"cultist",name:"Cultist",category:"humanoid",archetype:"support",signatureAbility:{id:"cultist_dark_blessing",name:"Dark Blessing",description:"Invokes dark powers to temporarily increase nearby allies' armor class.",damageDie:"d6",damageType:"necrotic",attackType:"spell",range:20,properties:["buff","ally","defense"]},baseStats:{STR:11,DEX:12,CON:12,INT:13,WIS:11,CHA:14},baseHP:9,baseAC:13,baseSpeed:30,audioPreference:{bass:.3,mid:.7,treble:.2},resistances:{resistances:["necrotic"]}},{id:"bear",name:"Bear",category:"beast",archetype:"brute",signatureAbility:{id:"bear_maul",name:"Maul",description:"A devastating multiattack with claws that can also grapple the target. Raw power embodied.",damageDie:"d6",damageType:"slashing",attackType:"melee",properties:["multiattack","grapple"]},baseStats:{STR:18,DEX:10,CON:16,INT:2,WIS:12,CHA:6},baseHP:34,baseAC:11,baseSpeed:40,audioPreference:{bass:.9,mid:.2,treble:.1},resistances:{resistances:["cold"]}},{id:"boar",name:"Boar",category:"beast",archetype:"brute",signatureAbility:{id:"boar_gore_charge",name:"Gore Charge",description:"A ferocious charge that deals bonus damage if the boar moves at least 20 feet before attacking.",damageDie:"d6",damageType:"piercing",attackType:"melee",properties:["charge"]},baseStats:{STR:13,DEX:11,CON:12,INT:2,WIS:8,CHA:4},baseHP:11,baseAC:11,baseSpeed:40,audioPreference:{bass:.8,mid:.3,treble:.1},resistances:{resistances:[]}},{id:"giant-spider",name:"Giant Spider",category:"beast",archetype:"archer",signatureAbility:{id:"spider_web_spray",name:"Web Spray",description:"Shoots sticky webbing at a target to restrain them. A restrained creature has speed 0 and can't use reactions.",damageDie:"d6",damageType:"poison",attackType:"ranged",range:60,properties:["control","restrain"]},baseStats:{STR:12,DEX:14,CON:12,INT:2,WIS:12,CHA:4},baseHP:26,baseAC:13,baseSpeed:30,audioPreference:{bass:.2,mid:.3,treble:.8},resistances:{resistances:["poison"]}},{id:"stirge",name:"Stirge",category:"beast",archetype:"archer",signatureAbility:{id:"stirge_blood_drain",name:"Blood Drain",description:"Attaches to target and drains blood, dealing damage and healing the stirge for half the damage dealt.",damageDie:"d6",damageType:"piercing",attackType:"melee",properties:["lifesteal","grapple"]},baseStats:{STR:4,DEX:14,CON:10,INT:1,WIS:6,CHA:2},baseHP:5,baseAC:12,baseSpeed:10,audioPreference:{bass:.1,mid:.2,treble:.9},resistances:{resistances:[]}}],Xe=[...mr,...lr,...cr,...ur,...dr,...hr,...pr];function gr(s){return Xe.find(e=>e.id===s)}function fr(s){return Xe.filter(e=>e.category===s)}function yr(s){return Xe.filter(e=>e.archetype===s)}const Ca={common:{statMultiplier:1,hpMultiplier:1,signatureDieSize:6,extraAbilityCount:0,hasResistances:!1},uncommon:{statMultiplier:1.08,hpMultiplier:1.3,signatureDieSize:8,extraAbilityCount:1,hasResistances:!1},elite:{statMultiplier:1.15,hpMultiplier:1.7,signatureDieSize:10,extraAbilityCount:2,hasResistances:!0},boss:{statMultiplier:1.25,hpMultiplier:2.2,signatureDieSize:12,extraAbilityCount:3,hasResistances:!0}};function Oe(s){return Ca[s]}function vr(s){return`1d${Ca[s].signatureDieSize}`}function Ts(){return["common","uncommon","elite","boss"]}function br(s){const e=Ts(),t=e.indexOf(s);if(!(t===-1||t===e.length-1))return e[t+1]}function wr(s){return s}const Sr=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],_r=Sr.map(wr);function Cs(s){return s}const Ar=["Barbarian","Bard","Cleric","Druid","Fighter","Monk","Paladin","Ranger","Rogue","Sorcerer","Warlock","Wizard"],Re=Ar.map(Cs);function ra(s){if(typeof s!="string")return!1;if(Re.includes(s))return!0;try{const t=require("../extensions/ExtensionManager.js").ExtensionManager.getInstance();if(t.get("classes")?.includes(s))return!0;const i=t.get("classes.data");if(i&&Array.isArray(i)&&i.map(r=>r.name).includes(s))return!0}catch{}return!1}const lt=["STR","DEX","CON","INT","WIS","CHA"];function Pe(s){return lt.includes(s)}const Is=["http://","https://","/","assets/"];function Es(s){return typeof s!="string"||s.trim()===""?!1:Is.some(e=>s.startsWith(e))}function hi(s,e){return s===void 0?{valid:!0,errors:[]}:typeof s!="string"?{valid:!1,errors:[`${e} must be a string`]}:s.trim()===""?{valid:!1,errors:[`${e} cannot be an empty string`]}:Es(s)?{valid:!0,errors:[]}:{valid:!1,errors:[`${e} must start with one of: ${Is.join(", ")} (got: "${s}")`]}}function Be(s){const e=[],t=hi(s.icon,"icon");t.valid||e.push(...t.errors);const a=hi(s.image,"image");return a.valid||e.push(...a.errors),e}const pi=["passive","active","resource","trigger"],mt=["default","custom"],mi=["stat_bonus","skill_proficiency","ability_unlock","passive_modifier","resource_grant","spell_slot_bonus"],gt=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],ft=["athletics","acrobatics","sleight_of_hand","stealth","arcana","history","investigation","nature","religion","animal_handling","insight","medicine","perception","survival","deception","intimidation","performance","persuasion"],gi=["none","proficient","expertise"];class Fe{static validateClassFeature(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Class feature must be an object"]};const i=e;if(!i.id||typeof i.id!="string"?a.push("Feature must have a valid id (string)"):this.isValidFeatureId(i.id)||a.push(`Feature id must use lowercase_with_underscores format: "${i.id}"`),(!i.name||typeof i.name!="string")&&a.push("Feature must have a valid name (string)"),(!i.description||typeof i.description!="string")&&a.push("Feature must have a description (string)"),!i.type||typeof i.type!="string"?a.push("Feature must have a type (string)"):pi.includes(i.type)||a.push(`Invalid type: "${i.type}". Must be one of: ${pi.join(", ")}`),!i.class||typeof i.class!="string"?a.push("Feature must have a class (string)"):ra(i.class)||a.push(`Invalid class: "${i.class}". Must be one of: ${Re.join(", ")} or a custom class registered via ExtensionManager.`),typeof i.level!="number"?a.push("Feature must have a level (number)"):(i.level<1||i.level>20)&&a.push(`Feature level must be between 1 and 20 (got: ${i.level})`),!i.source||typeof i.source!="string"?a.push("Feature must have a source (string)"):mt.includes(i.source)||a.push(`Invalid source: "${i.source}". Must be one of: ${mt.join(", ")}`),i.tags!==void 0)if(!Array.isArray(i.tags))a.push("Feature tags must be an array");else for(const r of i.tags)typeof r!="string"&&a.push(`Feature tag must be a string (got: ${typeof r})`);i.lore!==void 0&&typeof i.lore!="string"&&a.push("Feature lore must be a string"),i.subrace!==void 0&&typeof i.subrace!="string"&&a.push("Feature subrace must be a string");const n=Be({icon:i.icon,image:i.image});if(a.push(...n.map(r=>`Feature ${r}`)),i.prerequisites!==void 0){const r=this.validatePrerequisites(i.prerequisites,t);r.valid||a.push(...r.errors.map(o=>`Prerequisites: ${o}`))}if(i.effects!==void 0)if(!Array.isArray(i.effects))a.push("Feature effects must be an array");else for(let r=0;r<i.effects.length;r++){const o=this.validateEffect(i.effects[r]);o.valid||a.push(...o.errors.map(l=>`Effect ${r}: ${l}`))}return{valid:a.length===0,errors:a}}static validateRacialTrait(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Racial trait must be an object"]};const i=e;if(!i.id||typeof i.id!="string"?a.push("Trait must have a valid id (string)"):this.isValidFeatureId(i.id)||a.push(`Trait id must use lowercase_with_underscores format: "${i.id}"`),(!i.name||typeof i.name!="string")&&a.push("Trait must have a valid name (string)"),(!i.description||typeof i.description!="string")&&a.push("Trait must have a description (string)"),!i.race||typeof i.race!="string")a.push("Trait must have a race (string)");else{const r=gt.includes(i.race),o=t?.customRaces??[],l=o.includes(i.race);if(!r&&!l){const c=o.length>0?`, ${o.join(", ")}`:"";a.push(`Invalid race: "${i.race}". Must be one of: ${gt.join(",")}${c} or a custom race registered via ExtensionManager.`)}}if(!i.source||typeof i.source!="string"?a.push("Trait must have a source (string)"):mt.includes(i.source)||a.push(`Invalid source: "${i.source}". Must be one of: ${mt.join(", ")}`),i.tags!==void 0)if(!Array.isArray(i.tags))a.push("Trait tags must be an array");else for(const r of i.tags)typeof r!="string"&&a.push(`Trait tag must be a string (got: ${typeof r})`);i.lore!==void 0&&typeof i.lore!="string"&&a.push("Trait lore must be a string"),i.subrace!==void 0&&typeof i.subrace!="string"&&a.push("Trait subrace must be a string");const n=Be({icon:i.icon,image:i.image});if(a.push(...n.map(r=>`Trait ${r}`)),i.prerequisites!==void 0){const r=this.validatePrerequisites(i.prerequisites,t);r.valid||a.push(...r.errors.map(o=>`Prerequisites: ${o}`))}if(i.effects!==void 0)if(!Array.isArray(i.effects))a.push("Trait effects must be an array");else for(let r=0;r<i.effects.length;r++){const o=this.validateEffect(i.effects[r]);o.valid||a.push(...o.errors.map(l=>`Effect ${r}: ${l}`))}return{valid:a.length===0,errors:a}}static validateEffect(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Effect must be an object"]};const a=e;if(!a.type||typeof a.type!="string"?t.push("Effect must have a type (string)"):mi.includes(a.type)||t.push(`Invalid effect type: "${a.type}". Must be one of: ${mi.join(", ")}`),!a.target||typeof a.target!="string")t.push("Effect must have a target (string)");else{const i=a.type,n=a.target;if(!(i==="stat_bonus"&&this.isAbility(n))){if(i==="skill_proficiency"&&!ft.includes(n))t.push(`Invalid skill target: "${n}". Must be one of: ${ft.join(", ")}`);else if(i==="skill_proficiency"){const r=a.value;typeof r=="string"&&!gi.includes(r)&&t.push(`Invalid proficiency level: "${r}". Must be one of: ${gi.join(", ")}`)}}}return a.value===void 0&&t.push("Effect must have a value"),a.condition!==void 0&&typeof a.condition!="string"&&t.push("Effect condition must be a string"),a.description!==void 0&&typeof a.description!="string"&&t.push("Effect description must be a string"),{valid:t.length===0,errors:t}}static validatePrerequisites(e,t){const a=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Prerequisites must be an object"]};const i=e;if(i.level!==void 0&&(typeof i.level!="number"?a.push("Prerequisite level must be a number"):(i.level<1||i.level>20)&&a.push(`Prerequisite level must be between 1 and 20 (got: ${i.level})`)),i.abilities!==void 0)if(typeof i.abilities!="object"||Array.isArray(i.abilities)||i.abilities===null)a.push("Prerequisite abilities must be a record");else for(const[n,r]of Object.entries(i.abilities))lt.includes(n)||a.push(`Invalid ability in prerequisites: "${n}". Must be one of: ${lt.join(", ")}`),(typeof r!="number"||r<1||r>20)&&a.push(`Ability score requirement for ${n} must be a number between 1 and 20`);if(i.class!==void 0){if(typeof i.class!="string")a.push("Prerequisite class must be a string");else if(!ra(i.class)){const n=t?.customClasses??[];if(!n.includes(i.class)){const o=n.length>0?`, ${n.join(", ")}`:"";a.push(`Invalid prerequisite class: "${i.class}". Must be one of: ${Re.join(",")}${o} or a custom class registered via ExtensionManager.`)}}}if(i.race!==void 0)if(typeof i.race!="string")a.push("Prerequisite race must be a string");else{const n=gt.includes(i.race),r=t?.customRaces??[],o=r.includes(i.race);if(!n&&!o){const l=r.length>0?`, ${r.join(", ")}`:"";a.push(`Invalid prerequisite race: "${i.race}". Must be one of: ${gt.join(",")}${l} or a custom race registered via ExtensionManager.`)}}if(i.features!==void 0)if(!Array.isArray(i.features))a.push("Prerequisite features must be an array");else for(const n of i.features)typeof n!="string"?a.push("Prerequisite feature IDs must be strings"):this.isValidFeatureId(n)||a.push(`Invalid prerequisite feature ID format: "${n}"`);if(i.subrace!==void 0&&(typeof i.subrace!="string"?a.push("Prerequisite subrace must be a string"):i.subrace.trim()===""&&a.push("Prerequisite subrace cannot be empty")),i.skills!==void 0)if(!Array.isArray(i.skills))a.push("Prerequisite skills must be an array");else for(const n of i.skills)typeof n!="string"?a.push("Prerequisite skill IDs must be strings"):ft.includes(n)||a.push(`Invalid prerequisite skill: "${n}". Must be one of: ${ft.join(", ")}`);if(i.spells!==void 0)if(!Array.isArray(i.spells))a.push("Prerequisite spells must be an array");else for(const n of i.spells)typeof n!="string"?a.push("Prerequisite spell names must be strings"):n.trim()===""&&a.push("Prerequisite spell name cannot be empty");return i.custom!==void 0&&typeof i.custom!="string"&&a.push("Prerequisite custom condition must be a string"),{valid:a.length===0,errors:a}}static validateClassFeatures(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateClassFeature(a);n.valid||t.push(`Feature at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of class features"]}}static validateRacialTraits(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateRacialTrait(a);n.valid||t.push(`Trait at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of racial traits"]}}static isAbility(e){return Pe(e)}static isValidFeatureId(e){return/^[a-z][a-z0-9_]*$/.test(e)}}function kr(s,e){return Fe.validateClassFeature(s,e)}function Tr(s,e){return Fe.validateRacialTrait(s,e)}function Cr(s){return Fe.validateClassFeatures(s)}function Ir(s){return Fe.validateRacialTraits(s)}function Er(s){const e=[];if(s==null)return{valid:!0,errors:[]};if(typeof s!="object"||Array.isArray(s))return{valid:!1,errors:["Prerequisites must be an object"]};const t=s;if(t.level!==void 0&&(typeof t.level!="number"?e.push("Prerequisite level must be a number"):t.level<1&&e.push(`Prerequisite level must be at least 1 (got: ${t.level})`)),t.casterLevel!==void 0&&(typeof t.casterLevel!="number"?e.push("Prerequisite casterLevel must be a number"):t.casterLevel<1&&e.push(`Prerequisite casterLevel must be at least 1 (got: ${t.casterLevel})`)),t.abilities!==void 0)if(typeof t.abilities!="object"||Array.isArray(t.abilities)||t.abilities===null)e.push("Prerequisite abilities must be a record");else for(const[a,i]of Object.entries(t.abilities))Pe(a)||e.push(`Invalid ability in prerequisites: "${a}". Must be one of: STR, DEX, CON, INT, WIS, CHA`),typeof i!="number"?e.push(`Ability score for ${a} must be a number (got: ${typeof i})`):(i<1||i>20)&&e.push(`Ability score for ${a} must be between 1 and 20 (got: ${i})`);if(t.class!==void 0&&typeof t.class!="string"&&e.push("Prerequisite class must be a string"),t.race!==void 0&&typeof t.race!="string"&&e.push("Prerequisite race must be a string"),t.subrace!==void 0&&(typeof t.subrace!="string"?e.push("Prerequisite subrace must be a string"):t.subrace.trim()===""&&e.push("Prerequisite subrace cannot be empty")),t.features!==void 0)if(!Array.isArray(t.features))e.push("Prerequisite features must be an array");else for(const a of t.features)typeof a!="string"&&e.push("Prerequisite feature IDs must be strings");if(t.skills!==void 0)if(!Array.isArray(t.skills))e.push("Prerequisite skills must be an array");else for(const a of t.skills)typeof a!="string"&&e.push("Prerequisite skill IDs must be strings");if(t.spells!==void 0)if(!Array.isArray(t.spells))e.push("Prerequisite spells must be an array");else for(const a of t.spells)typeof a!="string"?e.push("Prerequisite spell names must be strings"):a.trim()===""&&e.push("Prerequisite spell name cannot be empty");return t.custom!==void 0&&typeof t.custom!="string"&&e.push("Prerequisite custom condition must be a string"),{valid:e.length===0,errors:e}}function Ms(s,e){const t=[];if(!s)return{valid:!0,errors:[]};if(s.level!==void 0&&e.level<s.level&&t.push(`Requires level ${s.level} (current: ${e.level})`),s.casterLevel!==void 0&&e.level<s.casterLevel&&t.push(`Requires caster level ${s.casterLevel} (current: ${e.level})`),s.abilities)for(const[a,i]of Object.entries(s.abilities)){const n=e.ability_scores[a];(n===void 0||n<i)&&t.push(`Requires ${a} ${i}+ (current: ${n??0})`)}if(s.class!==void 0&&e.class!==s.class&&t.push(`Requires ${s.class} class (current: ${e.class})`),s.race!==void 0&&e.race!==s.race&&t.push(`Requires ${s.race} race (current: ${e.race})`),s.subrace!==void 0){const a=e.subrace||"";a!==s.subrace&&t.push(`Requires ${s.subrace} subrace (current: ${a||"none"})`)}if(s.skills&&s.skills.length>0)for(const a of s.skills){const i=e.skills[a];i!=="proficient"&&i!=="expertise"&&t.push(`Requires proficiency in ${a} (current: ${i??"none"})`)}if(s.features&&s.features.length>0){const a=e.class_features||[];for(const i of s.features)a.includes(i)||t.push(`Requires feature: ${i}`)}if(s.spells&&s.spells.length>0){const a=e.spells?.known_spells||[],i=e.spells?.cantrips||[],n=[...a,...i];for(const r of s.spells)n.includes(r)||t.push(`Requires spell: ${r}`)}return s.custom,{valid:t.length===0,errors:t}}const fi=["default","custom"],yi=["none","proficient","expertise"],vi=["class","background","feat","custom","racial","other"];class we{static validateSkill(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill must be an object"]};const a=e;if(!a.id||typeof a.id!="string"?t.push("Skill must have a valid id (string)"):this.isValidSkillId(a.id)||t.push(`Skill id must use lowercase_with_underscores format: "${a.id}"`),(!a.name||typeof a.name!="string")&&t.push("Skill must have a valid name (string)"),!a.ability||typeof a.ability!="string"?t.push("Skill must have an ability (string)"):lt.includes(a.ability)||t.push(`Invalid ability: "${a.ability}". Must be one of: ${lt.join(", ")}`),!a.source||typeof a.source!="string"?t.push("Skill must have a source (string)"):fi.includes(a.source)||t.push(`Invalid source: "${a.source}". Must be one of: ${fi.join(", ")}`),a.description!==void 0&&typeof a.description!="string"&&t.push("Skill description must be a string"),a.armorPenalty!==void 0&&typeof a.armorPenalty!="boolean"&&t.push("Skill armorPenalty must be a boolean"),a.customProperties!==void 0)if(typeof a.customProperties!="object"||Array.isArray(a.customProperties)||a.customProperties===null)t.push("Skill customProperties must be a record");else for(const[n,r]of Object.entries(a.customProperties)){const o=typeof r;if(!["string","number","boolean"].includes(o)&&!Array.isArray(r)&&t.push(`customProperties.${n} has invalid type: ${o}. Must be string, number, boolean, or string array`),Array.isArray(r))for(let l=0;l<r.length;l++)typeof r[l]!="string"&&t.push(`customProperties.${n}[${l}] must be a string`)}if(a.categories!==void 0)if(!Array.isArray(a.categories))t.push("Skill categories must be an array");else for(const n of a.categories)typeof n!="string"&&t.push(`Skill category must be a string (got: ${typeof n})`);if(a.tags!==void 0)if(!Array.isArray(a.tags))t.push("Skill tags must be an array");else for(const n of a.tags)typeof n!="string"&&t.push(`Skill tag must be a string (got: ${typeof n})`);a.lore!==void 0&&typeof a.lore!="string"&&t.push("Skill lore must be a string");const i=Be({icon:a.icon,image:a.image});return t.push(...i.map(n=>`Skill ${n}`)),{valid:t.length===0,errors:t}}static validateSkills(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSkill(a);n.valid||t.push(`Skill at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of skills"]}}static validateSkillProficiency(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill proficiency must be an object"]};const a=e;return!a.skillId||typeof a.skillId!="string"?t.push("Skill proficiency must have a valid skillId (string)"):this.isValidSkillId(a.skillId)||t.push(`Skill proficiency skillId must use lowercase_with_underscores format: "${a.skillId}"`),!a.level||typeof a.level!="string"?t.push("Skill proficiency must have a level (string)"):yi.includes(a.level)||t.push(`Invalid proficiency level: "${a.level}". Must be one of: ${yi.join(", ")}`),!a.source||typeof a.source!="string"?t.push("Skill proficiency must have a source (string)"):vi.includes(a.source)||t.push(`Invalid proficiency source: "${a.source}". Must be one of: ${vi.join(", ")}`),a.grantedBy!==void 0&&typeof a.grantedBy!="string"&&t.push("Skill proficiency grantedBy must be a string"),{valid:t.length===0,errors:t}}static validateSkillProficiencies(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSkillProficiency(a);n.valid||t.push(`Proficiency at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of skill proficiencies"]}}static validateSkillListDefinition(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Skill list definition must be an object"]};const a=e;if((!a.class||typeof a.class!="string")&&t.push("Skill list definition must have a class (string)"),typeof a.skillCount!="number"?t.push("Skill list definition must have a skillCount (number)"):(a.skillCount<0||!Number.isInteger(a.skillCount))&&t.push(`Skill list skillCount must be a non-negative integer (got: ${a.skillCount})`),!Array.isArray(a.availableSkills))t.push("Skill list definition must have availableSkills (array)");else{for(const i of a.availableSkills)typeof i!="string"?t.push(`availableSkills must contain only strings (got: ${typeof i})`):this.isValidSkillId(i)||t.push(`availableSkills contains invalid skill ID format: "${i}"`);typeof a.skillCount=="number"&&a.skillCount>a.availableSkills.length&&t.push(`skillCount (${a.skillCount}) cannot exceed availableSkills length (${a.availableSkills.length})`)}return a.hasExpertise!==void 0&&typeof a.hasExpertise!="boolean"&&t.push("Skill list hasExpertise must be a boolean"),a.expertiseCount!==void 0&&(typeof a.expertiseCount!="number"?t.push("Skill list expertiseCount must be a number"):a.expertiseCount<0||!Number.isInteger(a.expertiseCount)?t.push(`expertiseCount must be a non-negative integer (got: ${a.expertiseCount})`):Array.isArray(a.availableSkills)&&a.expertiseCount>a.availableSkills.length&&t.push(`expertiseCount (${a.expertiseCount}) cannot exceed availableSkills length (${a.availableSkills.length})`)),{valid:t.length===0,errors:t}}static isValidSkillId(e){return/^[a-z][a-z0-9_]*$/.test(e)}static isValidAbility(e){return Pe(e)}static validateSkillPrerequisites(e,t){return Ms(e,t)}}function Mr(s){return we.validateSkill(s)}function Dr(s){return we.validateSkills(s)}function Rr(s){return we.validateSkillProficiency(s)}function Pr(s){return we.validateSkillProficiencies(s)}function xr(s){return we.validateSkillListDefinition(s)}function Nr(s,e){return we.validateSkillPrerequisites(s,e)}const Yt=["Abjuration","Conjuration","Divination","Enchantment","Evocation","Illusion","Necromancy","Transmutation"],Zt=[0,1,2,3,4,5,6,7,8,9],bi=["V","S","M"];class De{static validateSpell(e){const t=[];if(!e||typeof e!="object"||Array.isArray(e))return{valid:!1,errors:["Spell must be an object"]};const a=e;if((!a.name||typeof a.name!="string")&&t.push("Spell must have a valid name (string)"),typeof a.level!="number"?t.push("Spell must have a level (number)"):Zt.includes(a.level)||t.push(`Invalid spell level: "${a.level}". Must be one of: ${Zt.join(", ")}`),!a.school||typeof a.school!="string"?t.push("Spell must have a school (string)"):Yt.includes(a.school)||t.push(`Invalid school: "${a.school}". Must be one of: ${Yt.join(", ")}`),(!a.casting_time||typeof a.casting_time!="string")&&t.push("Spell must have a casting_time (string)"),(!a.range||typeof a.range!="string")&&t.push("Spell must have a range (string)"),!Array.isArray(a.components))t.push("Spell must have components (array)");else for(const n of a.components)typeof n!="string"?t.push(`Spell component must be a string (got: ${typeof n})`):bi.includes(n)||t.push(`Invalid spell component: "${n}". Must be one of: ${bi.join(", ")}`);(!a.duration||typeof a.duration!="string")&&t.push("Spell must have a duration (string)"),a.id!==void 0&&typeof a.id!="string"&&t.push("Spell id must be a string"),a.description!==void 0&&typeof a.description!="string"&&t.push("Spell description must be a string");const i=Be({icon:a.icon,image:a.image});if(t.push(...i.map(n=>`Spell ${n}`)),a.prerequisites!==void 0){const n=this.validatePrerequisites(a.prerequisites);n.valid||t.push(...n.errors.map(r=>`Prerequisites: ${r}`))}return{valid:t.length===0,errors:t}}static validateSpells(e){const t=[];return Array.isArray(e)?(e.forEach((a,i)=>{const n=this.validateSpell(a);n.valid||t.push(`Spell at index ${i}: ${n.errors.join("; ")}`)}),{valid:t.length===0,errors:t}):{valid:!1,errors:["Input must be an array of spells"]}}static validatePrerequisites(e){return Er(e)}static validateSpellPrerequisites(e,t){return Ms(e,t)}static isValidAbility(e){return Pe(e)}static isValidSchool(e){return Yt.includes(e)}static isValidSpellLevel(e){return Zt.includes(e)}}function $r(s){return De.validateSpell(s)}function Lr(s){return De.validateSpells(s)}function Or(s){return De.validatePrerequisites(s)}function Br(s,e){return De.validateSpellPrerequisites(s,e)}class pe{constructor(){this.allSkillsCache=null,this.abilityCache=null,this.categoryCache=null,this.manager=R.getInstance()}static getInstance(){return pe.instance||(pe.instance=new pe),pe.instance}invalidateCache(){this.allSkillsCache=null,this.abilityCache=null,this.categoryCache=null}getSkill(e){return this.getAllSkills().find(a=>a.id===e)}getAllSkills(){if(!this.allSkillsCache){const e=this.manager.get("skills");this.allSkillsCache=e}return this.allSkillsCache}getSkillsByAbility(e){if(!this.abilityCache){this.abilityCache=new Map;const t=this.getAllSkills();for(const a of t)this.abilityCache.has(a.ability)||this.abilityCache.set(a.ability,[]),this.abilityCache.get(a.ability).push(a)}return this.abilityCache.get(e)||[]}getSkillsByCategory(e){if(!this.categoryCache){this.categoryCache=new Map;const t=this.getAllSkills();for(const a of t)if(a.categories&&a.categories.length>0)for(const i of a.categories)this.categoryCache.has(i)||this.categoryCache.set(i,[]),this.categoryCache.get(i).push(a)}return this.categoryCache.get(e)||[]}getCategories(){const e=this.getAllSkills(),t=new Set;for(const a of e)if(a.categories)for(const i of a.categories)t.add(i);return Array.from(t)}getSkillsBySource(e){return this.getAllSkills().filter(t=>t.source===e)}isValidSkill(e){return this.getSkill(e)!==void 0}validateSkill(e){return we.validateSkill(e)}validatePrerequisites(e,t){return we.validateSkillPrerequisites(e.prerequisites,t)}getQueryStats(){const e=this.getAllSkills(),t=e.filter(r=>r.source==="default"),a=e.filter(r=>r.source==="custom"),i={STR:0,DEX:0,CON:0,INT:0,WIS:0,CHA:0};for(const r of e)i[r.ability]++;const n=this.getCategories();return{totalSkills:e.length,defaultSkills:t.length,customSkills:a.length,skillsByAbility:i,categories:n}}getSkillCount(){return this.getAllSkills().length}getAvailableSkills(e){return this.getAllSkills().filter(t=>t.prerequisites?this.validatePrerequisites(t,e).valid:!0)}}function Fr(){return pe.getInstance()}const it=["STR","DEX","CON","INT","WIS","CHA"],wi=["weapon","armor","item","box"],Si=["common","uncommon","rare","very_rare","legendary"],_i=["stat_bonus","skill_proficiency","ability_unlock","passive_modifier","special_property","damage_bonus","stat_requirement"],Ai=["vs_creature_type","at_time_of_day","wielder_race","wielder_class","while_equipped","on_hit","on_damage_taken","custom"],ki=["day","night","dawn","dusk"],Ti=["proficient","expertise"],Ci=["default","custom"],yt=/^\d+d\d+([+-]\d+)?$/,Hr=/^range_\d+_\d+$/;class Dt{static validateEquipment(e){const t=[];if((!e.name||typeof e.name!="string")&&t.push("Equipment must have a valid name (required)"),(!e.type||!wi.includes(e.type))&&t.push(`Equipment type must be one of: ${wi.join(", ")}`),(!e.rarity||!Si.includes(e.rarity))&&t.push(`Equipment rarity must be one of: ${Si.join(", ")}`),(typeof e.weight!="number"||e.weight<0)&&t.push("Equipment weight must be a non-negative number"),e.source!==void 0&&!Ci.includes(e.source)&&t.push(`Equipment source must be one of: ${Ci.join(", ")}`),e.properties)if(!Array.isArray(e.properties))t.push("Equipment properties must be an array");else for(let n=0;n<e.properties.length;n++){const r=this.validateProperty(e.properties[n]);r.valid||t.push(`Property at index ${n}: ${r.errors?.join(", ")}`)}if(e.grantsFeatures)if(!Array.isArray(e.grantsFeatures))t.push("grantsFeatures must be an array");else for(let n=0;n<e.grantsFeatures.length;n++){const r=e.grantsFeatures[n],o=this.validateFeatureReference(r,n);o.valid||t.push(...o.errors||[])}if(e.grantsSkills)if(!Array.isArray(e.grantsSkills))t.push("grantsSkills must be an array");else for(let n=0;n<e.grantsSkills.length;n++){const r=e.grantsSkills[n],o=this.validateSkillReference(r.skillId,n);o.valid||t.push(...o.errors||[]),r.level&&!Ti.includes(r.level)&&t.push(`grantsSkills[${n}]: proficiency level must be 'proficient' or 'expertise'`)}if(e.grantsSpells)if(!Array.isArray(e.grantsSpells))t.push("grantsSpells must be an array");else for(let n=0;n<e.grantsSpells.length;n++){const r=e.grantsSpells[n];(!r.spellId||typeof r.spellId!="string")&&t.push(`grantsSpells[${n}]: must have a valid spellId`),r.uses!==void 0&&(typeof r.uses!="number"||r.uses<0)&&t.push(`grantsSpells[${n}]: uses must be a non-negative number`),r.level!==void 0&&(typeof r.level!="number"||r.level<0)&&t.push(`grantsSpells[${n}]: level must be a non-negative number`)}if(e.damage){const n=this.validateDamageInfo(e.damage);n.valid||t.push(...n.errors||[])}const a=e.acBonus??e.armor_class;if(a!==void 0&&typeof a!="number"&&t.push("AC bonus must be a number"),e.weaponProperties&&(Array.isArray(e.weaponProperties)||t.push("weaponProperties must be an array")),e.spawnWeight!==void 0){const n=this.validateSpawnWeight(e.spawnWeight);n.valid||t.push(...n.errors||[])}if(e.templateId!==void 0&&typeof e.templateId!="string"&&t.push("templateId must be a string"),e.tags&&(Array.isArray(e.tags)||t.push("tags must be an array")),e.type==="box"&&!e.boxContents&&t.push('Equipment of type "box" must have a boxContents property'),e.boxContents!==void 0){const n=this.validateBoxContents(e.boxContents);n.valid||t.push(...n.errors||[])}const i=Be({icon:e.icon,image:e.image});return t.push(...i.map(n=>`Equipment ${n}`)),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateProperty(e){const t=[];if((!e.type||!_i.includes(e.type))&&t.push(`Property type must be one of: ${_i.join(", ")}`),(!e.target||typeof e.target!="string")&&t.push("Property must have a valid target"),e.value===void 0)t.push("Property must have a value");else{const a=this.validatePropertyValue(e.type,e.target,e.value);a.valid||t.push(...a.errors||[])}if(e.condition){const a=this.validateCondition(e.condition);a.valid||t.push(...a.errors||[])}return e.stackable!==void 0&&typeof e.stackable!="boolean"&&t.push("stackable must be a boolean"),e.description!==void 0&&typeof e.description!="string"&&t.push("description must be a string"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validatePropertyValue(e,t,a){const i=[];switch(e){case"stat_bonus":it.includes(t)||i.push(`stat_bonus target must be a valid ability: ${it.join(", ")}`),typeof a!="number"&&i.push("stat_bonus value must be a number");break;case"skill_proficiency":typeof t!="string"?i.push("skill_proficiency target must be a string"):pe.getInstance().isValidSkill(t)||i.push(`skill_proficiency target must be a valid skill ID: "${t}" not found in SkillQuery`),a!=="proficient"&&a!=="expertise"&&typeof a!="boolean"&&i.push('skill_proficiency value must be "proficient", "expertise", or a boolean');break;case"ability_unlock":(typeof t!="string"||t.length===0)&&i.push("ability_unlock must have a valid target"),typeof a!="boolean"&&typeof a!="number"&&i.push("ability_unlock value must be a boolean or number");break;case"passive_modifier":!["ac","armor_class","speed","max_hp","hp_max","initiative","saving_throws","attack_roll","damage_roll"].includes(t)&&!it.includes(t)&&pe.getInstance().isValidSkill(t),typeof a!="number"&&typeof a!="boolean"&&i.push("passive_modifier value must be a number or boolean");break;case"stat_requirement":it.includes(t)||i.push(`stat_requirement target must be a valid ability: ${it.join(", ")}`),typeof a!="number"&&i.push("stat_requirement value must be a number (minimum score)");break;case"special_property":(typeof t!="string"||t.length===0)&&i.push("special_property must have a valid target");break;case"damage_bonus":(typeof t!="string"||t.length===0)&&i.push("damage_bonus must have a valid target (damage type)"),typeof a=="string"&&!yt.test(a)?i.push('damage_bonus value must be a dice format (e.g., "1d6") or a number'):typeof a!="number"&&typeof a!="string"&&i.push("damage_bonus value must be a number or dice string");break;default:i.push(`Unknown property type: ${e}`)}return{valid:i.length===0,errors:i.length>0?i:void 0}}static validateCondition(e){const t=[];(!e.type||!Ai.includes(e.type))&&t.push(`Condition type must be one of: ${Ai.join(", ")}`);const a=e.type,i=e.value;switch(a){case"at_time_of_day":ki.includes(e.value)||t.push(`at_time_of_day value must be one of: ${ki.join(", ")}`);break;case"wielder_race":case"wielder_class":case"vs_creature_type":(typeof i!="string"||i.length===0)&&t.push(`${a} condition must have a valid string value`);break;case"while_equipped":case"on_hit":case"on_damage_taken":typeof i!="boolean"&&t.push(`${a} condition must have a boolean value`);break;case"custom":(typeof i!="string"||i.length===0)&&t.push("custom condition must have a valid value string"),(!e.description||typeof e.description!="string")&&t.push("custom condition must have a description");break;default:t.push(`Unknown condition type: ${a}`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}static validateFeatureReference(e,t){const a=[];if(typeof e=="string"){const i=re.getInstance(),n=i.getClassFeatureById(e),r=n?void 0:i.getRacialTraitById(e);!n&&!r&&a.push(`grantsFeatures[${t}]: Feature "${e}" not found in FeatureQuery`)}else if(typeof e=="object"&&e!==null){if((!e.id||typeof e.id!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid id`),(!e.name||typeof e.name!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid name`),(!e.description||typeof e.description!="string")&&a.push(`grantsFeatures[${t}]: Mini-feature must have a valid description`),!e.effects||!Array.isArray(e.effects))a.push(`grantsFeatures[${t}]: Mini-feature must have an effects array`);else for(let i=0;i<e.effects.length;i++){const n=this.validateProperty(e.effects[i]);n.valid||a.push(`grantsFeatures[${t}].effects[${i}]: ${n.errors?.join(", ")}`)}e.source!=="equipment_inline"&&a.push(`grantsFeatures[${t}]: Mini-feature must have source: 'equipment_inline'`)}else a.push(`grantsFeatures[${t}]: Feature reference must be a string or object`);return{valid:a.length===0,errors:a.length>0?a:void 0}}static validateEquipmentFeatureReference(e){const t=re.getInstance(),a=t.getClassFeatureById(e),i=a?void 0:t.getRacialTraitById(e);return!!(a||i)}static validateSkillReference(e,t){const a=[],i=t!==void 0?`grantsSkills[${t}]`:"skill";return!e||typeof e!="string"?(a.push(`${i}: must have a valid skillId`),{valid:!1,errors:a}):(pe.getInstance().isValidSkill(e)||a.push(`${i}: Skill "${e}" not found in SkillQuery`),{valid:a.length===0,errors:a.length>0?a:void 0})}static validateEquipmentSkillReference(e){return pe.getInstance().isValidSkill(e)}static validateDamageInfo(e){const t=[];if(!e)return{valid:!0};if(typeof e=="string"){const a=e.trim().split(/\s+/);if(a.length<2)t.push(`Damage string must be in format "NdM type" (e.g., "1d8 slashing"), got: "${e}"`);else{const i=a[0];yt.test(i)||t.push(`Damage dice must be in format "NdM" (e.g., "1d8"), got: ${i}`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}return!e.dice||typeof e.dice!="string"?t.push('Damage must have a valid dice string (e.g., "1d8")'):yt.test(e.dice)||t.push(`Damage dice must be in format "NdM" (e.g., "1d8", "2d6"), got: ${e.dice}`),(!e.damageType||typeof e.damageType!="string")&&t.push("Damage must have a valid damageType"),e.versatile!==void 0&&(typeof e.versatile!="string"?t.push("Damage versatile must be a string (dice format)"):yt.test(e.versatile)||t.push(`Damage versatile must be in dice format "NdM" (e.g., "1d10"), got: ${e.versatile}`)),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateSpawnWeight(e){const t=[];return typeof e!="number"?t.push("Spawn weight must be a number"):e<0?t.push("Spawn weight must be non-negative (0 = never random, still usable by game logic)"):Number.isFinite(e)||t.push("Spawn weight must be a finite number"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateBoxContents(e){const t=[];if(!e||typeof e!="object")return t.push("boxContents must be an object"),{valid:!1,errors:t};if(!Array.isArray(e.drops))return t.push("boxContents.drops must be an array"),{valid:!1,errors:t};if(e.consumeOnOpen!==void 0&&typeof e.consumeOnOpen!="boolean"&&t.push("boxContents.consumeOnOpen must be a boolean"),e.openRequirements!==void 0)if(!Array.isArray(e.openRequirements))t.push("boxContents.openRequirements must be an array");else for(let a=0;a<e.openRequirements.length;a++){const i=this.validateBoxOpenRequirement(e.openRequirements[a]);i.valid||t.push(...(i.errors||[]).map(n=>`openRequirements[${a}]: ${n}`))}for(let a=0;a<e.drops.length;a++){const i=e.drops[a];if(!i||!Array.isArray(i.pool)){t.push(`boxContents.drops[${a}]: pool must be an array`);continue}if(i.pool.length===0){t.push(`boxContents.drops[${a}]: pool must not be empty`);continue}let n=0;for(let r=0;r<i.pool.length;r++){const o=i.pool[r],l=`boxContents.drops[${a}].pool[${r}]`;typeof o.weight!="number"||o.weight<0?t.push(`${l}: weight must be a non-negative number`):n+=o.weight,o.itemName!==void 0&&o.gold!==void 0&&t.push(`${l}: itemName and gold are mutually exclusive`),o.itemName===void 0&&o.gold===void 0&&t.push(`${l}: must have either itemName or gold`),o.itemName!==void 0&&(typeof o.itemName!="string"||o.itemName.length===0?t.push(`${l}: itemName must be a non-empty string`):R.getInstance().get("equipment").some(h=>h.name===o.itemName)||t.push(`${l}: itemName "${o.itemName}" not found in equipment registry`)),o.gold!==void 0&&(typeof o.gold!="number"||o.gold<0)&&t.push(`${l}: gold must be a non-negative number`),o.quantity!==void 0&&(typeof o.quantity!="number"||o.quantity<1||!Number.isInteger(o.quantity))&&t.push(`${l}: quantity must be a positive integer`)}i.pool.length>0&&Math.abs(n-100)>.001&&t.push(`boxContents.drops[${a}]: pool weights sum to ${n}, expected 100`)}return{valid:t.length===0,errors:t.length>0?t:void 0}}static validateBoxOpenRequirement(e){const t=[];return!e||typeof e!="object"?(t.push("BoxOpenRequirement must be an object"),{valid:!1,errors:t}):(!e.itemName||typeof e.itemName!="string"||e.itemName.length===0?t.push("BoxOpenRequirement.itemName must be a non-empty string"):R.getInstance().get("equipment").some(r=>r.name===e.itemName)||t.push(`BoxOpenRequirement.itemName "${e.itemName}" not found in equipment registry`),e.quantity!==void 0&&(typeof e.quantity!="number"||e.quantity<1||!Number.isInteger(e.quantity))&&t.push("BoxOpenRequirement.quantity must be a positive integer"),{valid:t.length===0,errors:t.length>0?t:void 0})}static validateModification(e){const t=[];if((!e.id||typeof e.id!="string")&&t.push("Modification must have a valid id"),(!e.name||typeof e.name!="string")&&t.push("Modification must have a valid name"),(!e.appliedAt||typeof e.appliedAt!="string")&&t.push("Modification must have a valid appliedAt timestamp"),(!e.source||typeof e.source!="string")&&t.push("Modification must have a valid source"),!e.properties||!Array.isArray(e.properties))t.push("Modification must have a properties array");else for(let a=0;a<e.properties.length;a++){const i=this.validateProperty(e.properties[a]);i.valid||t.push(`Modification properties[${a}]: ${i.errors?.join(", ")}`)}if(e.addsFeatures)if(!Array.isArray(e.addsFeatures))t.push("Modification addsFeatures must be an array");else for(let a=0;a<e.addsFeatures.length;a++){const i=this.validateFeatureReference(e.addsFeatures[a],a);i.valid||t.push(`Modification addsFeatures[${a}]: ${i.errors?.join(", ")}`)}if(e.addsSkills)if(!Array.isArray(e.addsSkills))t.push("Modification addsSkills must be an array");else for(let a=0;a<e.addsSkills.length;a++){const i=e.addsSkills[a],n=this.validateSkillReference(i.skillId,a);n.valid||t.push(...(n.errors||[]).map(r=>`Modification ${r}`)),i.level&&!Ti.includes(i.level)&&t.push(`Modification addsSkills[${a}]: proficiency level must be 'proficient' or 'expertise'`)}return e.addsSpells&&(Array.isArray(e.addsSpells)||t.push("Modification addsSpells must be an array")),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateMiniFeature(e){const t=[];if((!e.id||typeof e.id!="string")&&t.push("Mini-feature must have a valid id"),(!e.name||typeof e.name!="string")&&t.push("Mini-feature must have a valid name"),(!e.description||typeof e.description!="string")&&t.push("Mini-feature must have a valid description"),!e.effects||!Array.isArray(e.effects))t.push("Mini-feature must have an effects array");else for(let a=0;a<e.effects.length;a++){const i=this.validateProperty(e.effects[a]);i.valid||t.push(`Mini-feature effects[${a}]: ${i.errors?.join(", ")}`)}return e.source!=="equipment_inline"&&t.push('Mini-feature must have source: "equipment_inline"'),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateACBonus(e){const t=[];return typeof e!="number"?t.push("AC bonus must be a number"):e<0?t.push("AC bonus cannot be negative"):Number.isFinite(e)||t.push("AC bonus must be a finite number"),{valid:t.length===0,errors:t.length>0?t:void 0}}static validateWeaponProperties(e){const t=[];if(!Array.isArray(e))return t.push("Weapon properties must be an array"),{valid:!1,errors:t};for(let a=0;a<e.length;a++){const i=e[a];if(typeof i!="string"){t.push(`Weapon property at index ${a} must be a string`);continue}i.startsWith("range_")&&(Hr.test(i)||t.push(`Weapon property "${i}" at index ${a} must be in format "range_MIN_MAX" (e.g., "range_20_60")`))}return{valid:t.length===0,errors:t.length>0?t:void 0}}}const Ce={Barbarian:{primary_ability:"STR",hit_die:12,saving_throws:["STR","CON"],is_spellcaster:!1,skill_count:2,available_skills:["athletics","animal_handling","intimidation","nature","perception","survival"],has_expertise:!1,description:"A fierce warrior of primitive background who can enter a battle rage. Barbarians draw on primal fury to unleash devastating attacks, shrug off wounds that would fell others, and channel their totem spirits' power. They thrive on the front lines, where their rage makes them unstoppable forces of nature."},Bard:{primary_ability:"CHA",hit_die:8,saving_throws:["DEX","CHA"],is_spellcaster:!0,skill_count:3,available_skills:["athletics","acrobatics","sleight_of_hand","stealth","arcana","history","investigation","nature","religion","animal_handling","insight","medicine","perception","survival","deception","intimidation","performance","persuasion"],has_expertise:!0,expertise_count:2,description:"An inspiring magician whose power echoes the music of creation. Bards weave magic through music, poetry, and performance, inspiring allies, demoralizing enemies, and wielding versatile spells. They are jack-of-all-trades who can fill any role, from frontline support to devastating spellcaster."},Cleric:{primary_ability:"WIS",hit_die:8,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["history","insight","medicine","persuasion","religion"],has_expertise:!1,description:"A priestly champion who wields divine magic in service of a higher power. Clerics serve as intermediaries between the mortal world and the divine, wielding healing and destructive magic granted by their deities. They can wear heavy armor, wield powerful weapons, and specialize in different domains of divine influence."},Druid:{primary_ability:"WIS",hit_die:8,saving_throws:["INT","WIS"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","animal_handling","insight","medicine","nature","perception","religion","survival"],has_expertise:!1,description:"A priest of the Old Faith, wielding the powers of nature and adopting animal forms. Druids revere nature and gain their magic from the natural world, commanding the elements, transforming into beasts, and calling upon the forces of life and growth. They serve as guardians of the wild places and mediators between civilization and nature."},Fighter:{primary_ability:"STR",hit_die:10,saving_throws:["STR","CON"],is_spellcaster:!1,skill_count:2,available_skills:["acrobatics","animal_handling","athletics","history","insight","intimidation","perception","survival"],has_expertise:!1,description:"A master of martial combat, skilled with a variety of weapons and armor. Fighters learn countless fighting techniques, specializing in certain styles of combat. They are the backbone of any adventuring party, capable of withstanding tremendous punishment while dishing out devastating damage through martial prowess alone."},Monk:{primary_ability:"DEX",hit_die:8,saving_throws:["STR","DEX"],is_spellcaster:!1,skill_count:2,available_skills:["acrobatics","athletics","history","insight","religion","stealth"],has_expertise:!1,description:"A master of martial arts, harnessing the power of the body in pursuit of physical and spiritual perfection. Monks use their bodies as weapons, channeling ki energy to perform extraordinary feats. They are agile warriors who can strike with deadly precision, evade attacks with supernatural grace, and harness mystical abilities."},Paladin:{primary_ability:"STR",hit_die:10,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["athletics","insight","intimidation","medicine","persuasion","religion"],has_expertise:!1,description:"A holy warrior bound to a sacred oath. Paladins combine martial prowess with divine magic, smiting foes with holy fire and healing allies with sacred power. They are paragons of their beliefs, radiating auras that protect allies and punish enemies. Their strength comes from their unwavering devotion to their oath."},Ranger:{primary_ability:"DEX",hit_die:10,saving_throws:["STR","DEX"],is_spellcaster:!0,skill_count:3,available_skills:["animal_handling","athletics","insight","investigation","nature","perception","stealth","survival"],has_expertise:!1,description:"A warrior who combats threats on the edges of civilization. Rangers are hunters and trackers who know the wilderness intimately, moving swiftly through natural terrain and striking from a distance. They develop supernatural connections to their chosen enemies and environments, gaining deadly precision against favored foes."},Rogue:{primary_ability:"DEX",hit_die:8,saving_throws:["DEX","INT"],is_spellcaster:!1,skill_count:4,available_skills:["acrobatics","athletics","deception","insight","intimidation","investigation","perception","performance","persuasion","sleight_of_hand","stealth"],has_expertise:!0,expertise_count:2,description:"A scoundrel who uses stealth and trickery to overcome obstacles and enemies. Rogues excel at skills, finding traps, and striking devastating blows from hiding. Whether as thieves, assassins, or spies, they are versatile problem-solvers who can disable threats before allies even know they exist."},Sorcerer:{primary_ability:"CHA",hit_die:6,saving_throws:["CON","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","deception","insight","intimidation","persuasion","religion"],has_expertise:!1,description:"A spellcaster who draws on inherent magic from a supernatural bloodline or cosmic source. Sorcerers have no need for spellbooks—their power comes from within, born of draconic heritage, wild magic, or other supernatural origins. They can manipulate and metamagic their spells in ways other casters cannot."},Warlock:{primary_ability:"CHA",hit_die:8,saving_throws:["WIS","CHA"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","deception","history","intimidation","investigation","nature","religion"],has_expertise:!1,description:"A wielder of magic that is derived from a bargain with an extraplanar entity. Warlocks gain their powers through pacts with powerful beings—fiends, fey, Great Old Ones, or other eldritch entities. In exchange for service, they receive mystical abilities and can cast eldritch invocations that blur the line between spell and supernatural ability."},Wizard:{primary_ability:"INT",hit_die:6,saving_throws:["INT","WIS"],is_spellcaster:!0,skill_count:2,available_skills:["arcana","history","insight","investigation","medicine","religion"],has_expertise:!1,description:"A scholarly magic-user capable of manipulating the structures of reality. Wizards are masters of arcane magic, studying spellbooks to unlock the secrets of the universe. They specialize in specific schools of magic, from evocation to illusion, and have access to the widest variety of spells of any class."}},Ds=["Barbarian","Bard","Cleric","Druid","Fighter","Monk","Paladin","Ranger","Rogue","Sorcerer","Warlock","Wizard"],Rs={Barbarian:{primary:"bass",secondary:"amplitude",bass:1,amplitude:.7},Fighter:{primary:"bass",secondary:"amplitude",bass:.9,amplitude:.8},Paladin:{primary:"bass",secondary:"mid",bass:.8,mid:.5},Rogue:{primary:"treble",treble:1},Ranger:{primary:"treble",secondary:"bass",treble:.8,bass:.5},Monk:{primary:"treble",secondary:"mid",treble:.7,mid:.6},Wizard:{primary:"mid",mid:1},Cleric:{primary:"mid",secondary:"amplitude",mid:.8,amplitude:.6},Druid:{primary:"mid",secondary:"bass",mid:.7,bass:.6},Bard:{primary:"amplitude",secondary:"mid",tertiary:"treble",amplitude:.8,mid:.6,treble:.3},Sorcerer:{primary:"amplitude",secondary:"chaos",amplitude:.9},Warlock:{primary:"amplitude",secondary:"treble",amplitude:.7,treble:.5}},Ia={"Acid Splash":{name:"Acid Splash",level:0,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"Hurls a bubble of acid at a creature or point. On hit, deals acid damage to creatures in a small splash radius. Dexterity save for half damage."},"Fire Bolt":{name:"Fire Bolt",level:0,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Hurls a mote of fire at a creature or object. On hit, deals fire damage. One of the most reliable damaging cantrips for ranged combat."},Light:{name:"Light",level:0,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","M"],duration:"1 hour",description:"Touch one object to make it shed bright light in a 20-foot radius and dim light for another 20 feet. Completely illuminates dark areas and can be moved with the touched object."},"Mage Hand":{name:"Mage Hand",level:0,school:"Conjuration",casting_time:"1 action",range:"30 feet",components:["V","S"],duration:"Concentration, up to 1 minute",description:"Conjures a spectral, floating hand at a point you choose. The hand can manipulate objects, open unlocked doors, or stow items. It cannot attack or activate magical items."},Mending:{name:"Mending",level:0,school:"Transmutation",casting_time:"1 minute",range:"Touch",components:["V","S","M"],duration:"Instantaneous",description:"Repairs a single break or tear in an object you touch, such as a broken chain link, two halves of a broken key, or a torn cloak. Cannot restore magic items or creatures."},Message:{name:"Message",level:0,school:"Transmutation",casting_time:"1 action",range:"120 feet",components:["V","S","M"],duration:"1 round",description:"Points your finger toward a creature within range and whisper a message. The target hears the message and can reply in a whisper that only you can hear. Perfect for covert communication."},Prestidigitation:{name:"Prestidigitation",level:0,school:"Transmutation",casting_time:"1 action",range:"10 feet",components:["V","S"],duration:"Up to 1 hour",description:"Performs minor magical tricks for your amusement. Can chill, warm, flavor, clean, or soil small objects. Create harmless sensory effects like colored lights or faint sounds."},"Sacred Flame":{name:"Sacred Flame",level:0,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"Calls down flame-like radiance on a target. The target must succeed on a Dexterity save or take radiant damage. This spell can be cast from behind cover and ignores half and three-quarters cover."},"Shocking Grasp":{name:"Shocking Grasp",level:0,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Instantaneous",description:"Lightning springs from your hand to deliver a shock to a creature. You have advantage on the attack roll if the target is wearing metal armor. The shock cannot be used to make reactions of opportunity."},"Vicious Mockery":{name:"Vicious Mockery",level:0,school:"Enchantment",casting_time:"1 action",range:"60 feet",components:["V"],duration:"Instantaneous",description:"Utters a cruel insult to a creature within range. The target must make a Wisdom save or take psychic damage. A creative and subtle way to attack enemies in social situations."},"Burning Hands":{name:"Burning Hands",level:1,school:"Evocation",casting_time:"1 action",range:"Self (15-foot cone)",components:["V","S"],duration:"Instantaneous",description:"Ignites a cone of fire from your outstretched hands. Each creature in the area must make a Dexterity save or take fire damage. Devastating damage at close range but dangerous to allies in the cone."},"Charm Person":{name:"Charm Person",level:1,school:"Enchantment",casting_time:"1 action",range:"30 feet",components:["V","S"],duration:"1 hour",description:"Attempts to charm a humanoid you can see. The target must make a Wisdom save or be charmed by you. While charmed, it regards you as a friendly acquaintance. The effect ends if you or your allies do anything harmful to it."},"Cure Wounds":{name:"Cure Wounds",level:1,school:"Evocation",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Instantaneous",description:"A creature you touch regains hit points equal to 1d8 plus your spellcasting ability modifier. The amount increases by 1d8 for each slot level above 1st. This spell has no effect on undead or constructs."},"Detect Magic":{name:"Detect Magic",level:1,school:"Divination",casting_time:"1 action",range:"Self",components:["V","S"],duration:"Concentration, up to 10 minutes",description:"For the duration, you sense the presence of magic within 30 feet of you. You can identify the school of magic and locate the source, but not learn its exact properties. Essential for identifying magical auras."},"Disguise Self":{name:"Disguise Self",level:1,school:"Illusion",casting_time:"1 action",range:"Self",components:["V","S"],duration:"1 hour",description:"Makes you—including your clothing, armor, weapons, and other belongings—look different until the spell ends. You can change your height, weight, facial features, voice, and even appear as another creature of the same size."},"Expeditious Retreat":{name:"Expeditious Retreat",level:1,school:"Transmutation",casting_time:"1 bonus action",range:"Self",components:["V","S"],duration:"Concentration, up to 10 minutes",description:"Doubles your walking speed and grants the ability to take the Disengage or Hide action as a bonus action on each turn. Essential for escape or closing the distance safely."},"False Life":{name:"False Life",level:1,school:"Necromancy",casting_time:"1 action",range:"Self",components:["V","S","M"],duration:"1 hour",description:"Bolsters your vitality with necromantic magic, gaining temporary hit points equal to 1d4 plus your spellcasting ability modifier. The temporary hit points increase by 1d4 for each slot level above 1st."},"Feather Fall":{name:"Feather Fall",level:1,school:"Transmutation",casting_time:"1 reaction",range:"60 feet",components:["V","M"],duration:"1 minute",description:"Choose up to five falling creatures within range. A falling creature's rate of descent slows to 60 feet per round until the spell ends. If the creature lands before the spell ends, it takes no falling damage and lands on its feet."},Grease:{name:"Grease",level:1,school:"Conjuration",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"1 minute",description:"Covers a 10-foot square on the ground in slippery grease. Creatures in the area must succeed on a Dexterity save or fall prone. Creatures entering the area or ending their turn there must also save or fall."},"Healing Word":{name:"Healing Word",level:1,school:"Evocation",casting_time:"1 bonus action",range:"60 feet",components:["V","S"],duration:"Instantaneous",description:"A creature of your choice that you can see within range regains hit points equal to 1d4 plus your spellcasting ability modifier. This spell has no effect on undead or constructs. Essential for saving fallen allies from a distance."},Identify:{name:"Identify",level:1,school:"Divination",casting_time:"1 minute",range:"Touch",components:["V","S","M"],duration:"Instantaneous",description:"Choose one object that you must touch throughout the casting. Learn if it's magically cursed or has been affected by a spell. You learn the properties and how to use them if it's a magic item. Requires a 100gp pearl focus."},"Mage Armor":{name:"Mage Armor",level:1,school:"Abjuration",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"8 hours",description:"Touch a willing creature to protect it with a magical force. The target's AC becomes 13 + its Dexterity modifier, minus any magical penalties. The spell ends if the target dons armor or if you dismiss it."},"Magic Missile":{name:"Magic Missile",level:1,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Creates three glowing darts of magical force. Each dart hits a creature of your choice that you can see within range. A dart deals 1d4 + 1 force damage. For each slot level above 1st, the spell creates one more dart."},Shield:{name:"Shield",level:1,school:"Abjuration",casting_time:"1 reaction",range:"Self",components:["V","S"],duration:"1 round",description:"An invisible barrier of magical force appears and protects you. Until the start of your next turn, you have a +5 bonus to AC, including against the triggering attack, and you take no damage from magic missile."},Sleep:{name:"Sleep",level:1,school:"Enchantment",casting_time:"1 action",range:"90 feet",components:["V","S","M"],duration:"1 minute",description:"Rolls 5d8; the total is how many hit points of creatures this spell can affect. Creatures in a 20-foot cube within range are affected in ascending order of current hit points. Unconscious creatures fall prone and are incapacitated."},Thunderwave:{name:"Thunderwave",level:1,school:"Evocation",casting_time:"1 action",range:"Self (15-foot cube)",components:["V","S"],duration:"Instantaneous",description:"A wave of thunderous force sweeps out from you. Each creature in the area must make a Constitution save or take thunder damage and be pushed 10 feet away. Unattended objects are also pushed and take damage."},"Acid Arrow":{name:"Acid Arrow",level:2,school:"Evocation",casting_time:"1 action",range:"90 feet",components:["V","S","M"],duration:"Instantaneous",description:"Hurls a bubble of acid at a point you choose within range. On a hit, the target takes acid damage immediately and additional acid damage at the end of its next turn. This secondary damage ignores resistance but not immunity."},"Aganazzar's Scorcher":{name:"Aganazzar's Scorcher",level:2,school:"Evocation",casting_time:"1 action",range:"30 feet",components:["V","S","M"],duration:"Instantaneous",description:"A line of roaring flame erupts from you in a 30-foot line. Each creature in the line must make a Dexterity save or take fire damage. A flamethrower-like effect that excels at controlling narrow corridors."},Blur:{name:"Blur",level:2,school:"Illusion",casting_time:"1 action",range:"Self",components:["V"],duration:"Concentration, up to 1 minute",description:"Your body becomes blurred, shifting and wavering to all who can see you. Until the spell ends, attacks against you have disadvantage, and you have advantage on Dexterity saving throws. Essential defense for spellcasters."},"Detect Thoughts":{name:"Detect Thoughts",level:2,school:"Divination",casting_time:"1 action",range:"Self",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"For the duration, you can read the thoughts of certain creatures. You initially sense the presence of thinking creatures, then can focus on one to read its surface thoughts. Deeper thoughts require a contest of Intelligence."},Fireball:{name:"Fireball",level:3,school:"Evocation",casting_time:"1 action",range:"150 feet",components:["V","S","M"],duration:"Instantaneous",description:"A bright streak flashes from your pointing finger to a point you choose within range and then blossoms with a low roar into an explosion of flame. Each creature in a 20-foot-radius sphere must make a Dexterity save or take fire damage."},"Hold Person":{name:"Hold Person",level:2,school:"Enchantment",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"Choose a humanoid that you can see within range. The target must succeed on a Wisdom save or be paralyzed. The spell can target additional humanoids at higher slot levels. Paralyzed creatures are automatically critical hits."},Invisibility:{name:"Invisibility",level:2,school:"Illusion",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"A creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person. The spell ends if the target attacks or casts a spell."},Knock:{name:"Knock",level:2,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V"],duration:"Instantaneous",description:"Choose an object that you can see within range. The object can be a door, a box, a chest, a set of manacles, a padlock, or another object that contains a mundane or magical means of preventing access."},"Misty Step":{name:"Misty Step",level:2,school:"Conjuration",casting_time:"1 bonus action",range:"Self",components:["V"],duration:"Instantaneous",description:"Briefly surrounded by silvery mist, you teleport up to 30 feet to an unoccupied space you can see. You can bring along objects as long as their weight doesn't exceed what you can carry. A quick escape or repositioning tool."},"Mirror Image":{name:"Mirror Image",level:2,school:"Illusion",casting_time:"1 action",range:"Self",components:["V","S"],duration:"Concentration, up to 1 minute",description:"Three illusory duplicates of yourself appear in your space. Until the spell ends, the duplicates move with you and mimic your actions, shifting position so it's impossible to track which image is real."},"Scorching Ray":{name:"Scorching Ray",level:2,school:"Evocation",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"You create three rays of fire and hurl them at targets within range. You can aim them at one target or several. Make a ranged spell attack for each ray. On a hit, the target takes fire damage."},Shatter:{name:"Shatter",level:2,school:"Evocation",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Instantaneous",description:"A sudden loud ringing noise, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution save or take thunder damage."},Suggestion:{name:"Suggestion",level:2,school:"Enchantment",casting_time:"1 action",range:"30 feet",components:["V","M"],duration:"Concentration, up to 1 minute",description:"Suggest a course of activity to a creature you can see within range and can understand. The target must make a Wisdom save or pursue the course of action. The suggestion must be worded so as to sound reasonable."},"Animate Dead":{name:"Animate Dead",level:3,school:"Necromancy",casting_time:"1 minute",range:"10 feet",components:["V","S","M"],duration:"Instantaneous",description:"This spell creates an undead servant. Choose a pile of bones or a corpse of a Medium or Small humanoid within range. Your spell animates it as a skeleton or a zombie that obeys your commands until destroyed."},Blink:{name:"Blink",level:3,school:"Transmutation",casting_time:"1 action",range:"Self",components:["V","S"],duration:"1 minute",description:"Roll a d20 at the start of each of your turns. On an 11 or higher, you vanish from your current plane of existence and appear in the Ethereal Plane for the round, returning to where you were. Attacks against you have disadvantage while blinking."},Counterspell:{name:"Counterspell",level:3,school:"Abjuration",casting_time:"1 reaction",range:"60 feet",components:["S"],duration:"Instantaneous",description:"You attempt to interrupt a creature in the process of casting a spell. The creature must make a Constitution save. If the spell is 3rd level or lower, it fails and has no effect. Higher level spells require an ability check."},"Dispel Magic":{name:"Dispel Magic",level:3,school:"Abjuration",casting_time:"1 action",range:"120 feet",components:["V","S"],duration:"Instantaneous",description:"Choose one creature, object, or magical effect within range. Any spell of 3rd level or lower on the target ends. For each spell of 4th level or higher, make an ability check using your spellcasting ability."},"Lightning Bolt":{name:"Lightning Bolt",level:3,school:"Evocation",casting_time:"1 action",range:"Self (100-foot line)",components:["V","S","M"],duration:"Instantaneous",description:"A stroke of lightning forming a line 100 feet long and 5 feet wide blasts out from you in a direction you choose. Each creature in the line must make a Dexterity save or take lightning damage."},"Major Image":{name:"Major Image",level:3,school:"Illusion",casting_time:"1 action",range:"120 feet",components:["V","S","M"],duration:"Concentration, up to 10 minutes",description:"You create the image of an object, a creature, or some other visible phenomenon that is no larger than a 20-foot cube. The image appears at a spot within range and lasts for the duration. The image includes sounds, smells, and temperature."},"Sleet Storm":{name:"Sleet Storm",level:3,school:"Evocation",casting_time:"1 action",range:"150 feet",components:["V","S","M"],duration:"Concentration, up to 1 minute",description:"Until the spell ends, freezing rain and sleet fall in a 20-foot-tall cylinder with a 40-foot radius centered on a point you choose within range. The area is heavily obscured, and the ground is difficult terrain."},Telekinesis:{name:"Telekinesis",level:3,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V","S"],duration:"Concentration, up to 1 minute",description:"You gain the ability to move or manipulate creatures or objects by thought. When you cast the spell, and as your action each round for the duration, you can exert your will on one creature or object."},"Dimension Door":{name:"Dimension Door",level:4,school:"Conjuration",casting_time:"1 action",range:"500 feet",components:["V"],duration:"Instantaneous",description:"You teleport yourself from your current location to any other spot within range. You arrive at exactly the spot desired. It can be a place you can see, one you can visualize, or one you can describe by stating distance and direction."},"Greater Invisibility":{name:"Greater Invisibility",level:4,school:"Illusion",casting_time:"1 action",range:"Touch",components:["V","S"],duration:"Concentration, up to 1 minute",description:"You or a creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person. Unlike regular invisibility, attacks do not end the spell."},Polymorph:{name:"Polymorph",level:4,school:"Transmutation",casting_time:"1 action",range:"60 feet",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"Transforms a creature into a different beast. The target's game statistics, including mental ability scores, are replaced by the statistics of the chosen beast. It retains its alignment and personality. The creature assumes the hit points of the new form."},Stoneskin:{name:"Stoneskin",level:4,school:"Abjuration",casting_time:"1 action",range:"Touch",components:["V","S","M"],duration:"Concentration, up to 1 hour",description:"The touched creature's skin becomes as hard as stone and covered in rocky growths. The target's AC becomes 15 + its Dexterity modifier (max 23), and it is resistant to nonmagical bludgeoning, piercing, and slashing damage."},"Cone of Cold":{name:"Cone of Cold",level:5,school:"Evocation",casting_time:"1 action",range:"Self (60-foot cone)",components:["V","S","M"],duration:"Instantaneous",description:"A blast of cold air erupts from your hands. Each creature in a 60-foot cone must make a Constitution saving throw. A creature takes 8d8 cold damage on a failed save, or half as much on a successful one."},"Teleportation Circle":{name:"Teleportation Circle",level:5,school:"Conjuration",casting_time:"1 minute",range:"10 feet",components:["V","M"],duration:"1 round",description:"As you cast the spell, you draw a circle on the ground with the chalk. When you complete the casting, a glowing portal opens within the circle. Any creature that enters the portal instantly appears within 5 feet of the destination circle."}},Rt={Wizard:{cantrips:["Acid Splash","Fire Bolt","Light","Mage Hand","Mending","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Feather Fall","Grease","Identify","Mage Armor","Magic Missile","Shield","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Shatter","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image","Sleet Storm","Telekinesis"],4:["Dimension Door","Greater Invisibility","Polymorph","Stoneskin"],5:["Cone of Cold","Teleportation Circle"]}},Sorcerer:{cantrips:["Acid Splash","Fire Bolt","Light","Mage Hand","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Feather Fall","Grease","Mage Armor","Magic Missile","Shield","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Shatter","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image","Sleet Storm"],4:["Dimension Door","Greater Invisibility","Polymorph","Stoneskin"],5:["Cone of Cold","Teleportation Circle"]}},Bard:{cantrips:["Light","Mage Hand","Mending","Message","Prestidigitation","Vicious Mockery"],spells_by_level:{1:["Charm Person","Detect Magic","Disguise Self","Expeditious Retreat","Feather Fall","Grease","Healing Word","Identify","Magic Missile","Sleep","Thunderwave"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Knock","Misty Step","Mirror Image","Scorching Ray","Suggestion"],3:["Animate Dead","Blink","Counterspell","Dispel Magic","Lightning Bolt","Major Image"],4:["Dimension Door","Greater Invisibility","Polymorph"],5:["Teleportation Circle"]}},Cleric:{cantrips:["Light","Mending","Message","Sacred Flame"],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Identify","Healing Word","Magic Missile","Shield","Sleep"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Scorching Ray"],3:["Animate Dead","Counterspell","Dispel Magic","Lightning Bolt"],4:["Dimension Door","Polymorph"],5:["Cone of Cold"]}},Druid:{cantrips:["Light","Mending","Prestidigitation"],spells_by_level:{1:["Burning Hands","Cure Wounds","Detect Magic","Expeditious Retreat","Feather Fall","Grease","Healing Word","Identify"],2:["Acid Arrow","Blur","Detect Thoughts","Hold Person","Invisibility","Scorching Ray","Shatter"],3:["Animate Dead","Counterspell","Dispel Magic","Lightning Bolt","Sleet Storm"],4:["Dimension Door","Polymorph"],5:["Cone of Cold","Teleportation Circle"]}},Paladin:{cantrips:[],spells_by_level:{1:["Burning Hands","Charm Person","Cure Wounds","Detect Magic","Feather Fall","Healing Word","Protection","Shield"],2:["Acid Arrow","Hold Person","Scorching Ray"],3:["Counterspell","Dispel Magic","Lightning Bolt"],4:["Dimension Door","Polymorph"],5:["Cone of Cold"]}},Ranger:{cantrips:[],spells_by_level:{1:["Detect Magic","Expeditious Retreat","Feather Fall","Grease","Identify"],2:["Blur","Hold Person","Invisibility","Misty Step"],3:["Blink","Counterspell","Dispel Magic"],4:["Dimension Door","Polymorph"],5:[]}},Warlock:{cantrips:["Acid Splash","Fire Bolt","Mage Hand","Message","Prestidigitation","Shocking Grasp"],spells_by_level:{1:["Burning Hands","Charm Person","Detect Magic","Disguise Self","Expeditious Retreat","False Life","Grease","Hex","Magic Missile"],2:["Acid Arrow","Darkness","Hold Person","Invisibility","Scorching Ray","Shatter"],3:["Counterspell","Dispel Magic","Fireball","Lightning Bolt"],4:["Dimension Door","Greater Invisibility"],5:["Cone of Cold"]}}},oa={Wizard:{1:{1:2},2:{1:3},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Sorcerer:{1:{1:2},2:{1:3},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Bard:{1:{1:2},2:{1:2,2:0},3:{1:3,2:0},4:{1:3,2:2},5:{1:4,2:2,3:0},6:{1:4,2:2,3:0},7:{1:4,2:2,3:2},8:{1:4,2:2,3:2,4:0},9:{1:4,2:3,3:2,4:0},10:{1:4,2:3,3:2,4:1},11:{1:4,2:3,3:3,4:1},12:{1:4,2:3,3:3,4:1},13:{1:4,2:3,3:3,4:2,5:0},14:{1:4,2:3,3:3,4:2,5:0},15:{1:4,2:3,3:3,4:2,5:1},16:{1:4,2:3,3:3,4:2,5:1},17:{1:4,2:3,3:3,4:2,5:1,6:0},18:{1:4,2:3,3:3,4:3,5:1,6:0},19:{1:4,2:3,3:3,4:3,5:2,6:1},20:{1:4,2:3,3:3,4:3,5:2,6:1}},Cleric:{1:{1:2},2:{1:3},3:{1:3,2:2},4:{1:4,2:2},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Druid:{1:{1:2},2:{1:3},3:{1:3,2:2},4:{1:4,2:2},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3,4:1},8:{1:4,2:3,3:3,4:2},9:{1:4,2:3,3:3,4:3,5:1},10:{1:4,2:3,3:3,4:3,5:2},11:{1:4,2:3,3:3,4:3,5:2,6:1},12:{1:4,2:3,3:3,4:3,5:2,6:1},13:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},14:{1:4,2:3,3:3,4:3,5:2,6:1,7:1},15:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},16:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1},17:{1:4,2:3,3:3,4:3,5:2,6:1,7:1,8:1,9:1},18:{1:4,2:3,3:3,4:3,5:3,6:1,7:1,8:1,9:1},19:{1:4,2:3,3:3,4:3,5:3,6:2,7:1,8:1,9:1},20:{1:4,2:3,3:3,4:3,5:3,6:2,7:2,8:1,9:1}},Paladin:{1:{1:0},2:{1:2},3:{1:3},4:{1:3},5:{1:4,2:2},6:{1:4,2:2},7:{1:4,2:3},8:{1:4,2:3},9:{1:4,2:3,3:2},10:{1:4,2:3,3:2},11:{1:4,2:3,3:3},12:{1:4,2:3,3:3},13:{1:4,2:3,3:3,4:1},14:{1:4,2:3,3:3,4:1},15:{1:4,2:3,3:3,4:2},16:{1:4,2:3,3:3,4:2},17:{1:4,2:3,3:3,4:3,5:1},18:{1:4,2:3,3:3,4:3,5:1},19:{1:4,2:3,3:3,4:3,5:2},20:{1:4,2:3,3:3,4:3,5:2}},Ranger:{1:{1:0},2:{1:2},3:{1:2,2:0},4:{1:3,2:0},5:{1:3,2:2},6:{1:3,2:2},7:{1:3,2:2,3:0},8:{1:3,2:2,3:0},9:{1:3,2:3,3:0},10:{1:4,2:3,3:0},11:{1:4,2:3,3:2},12:{1:4,2:3,3:2},13:{1:4,2:3,3:2,4:0},14:{1:4,2:3,3:2,4:0},15:{1:4,2:3,3:2,4:1},16:{1:4,2:3,3:2,4:1},17:{1:4,2:3,3:3,4:1},18:{1:4,2:3,3:3,4:1},19:{1:4,2:3,3:3,4:2},20:{1:4,2:3,3:3,4:2}},Warlock:{1:{1:1},2:{1:2},3:{1:2,2:2},4:{1:2,2:2},5:{1:2,2:2,3:2},6:{1:2,2:2,3:2},7:{1:2,2:2,3:2,4:1},8:{1:2,2:2,3:2,4:1},9:{1:2,2:2,3:2,4:2,5:1},10:{1:2,2:2,3:2,4:2,5:1},11:{1:3,2:3,3:3,4:3,5:1},12:{1:3,2:3,3:3,4:3,5:2},13:{1:3,2:3,3:3,4:3,5:2},14:{1:3,2:3,3:3,4:3,5:2},15:{1:3,2:3,3:3,4:3,5:3},16:{1:3,2:3,3:3,4:3,5:3},17:{1:4,2:4,3:4,4:4,5:4},18:{1:4,2:4,3:4,4:4,5:4},19:{1:4,2:4,3:4,4:4,5:4},20:{1:4,2:4,3:4,4:4,5:4}}};function Ea(s){if(s in Ce)return Ce[s];try{const t=R.getInstance().get("classes.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.name===s);if(a){if(a.baseClass&&a.baseClass in Ce){const i=Ce[a.baseClass];return{...i,...a,available_skills:a.available_skills||i.available_skills}}return a}}}catch{}}const ct={1:0,2:300,3:900,4:2700,5:6500,6:14e3,7:23e3,8:34e3,9:48e3,10:64e3,11:85e3,12:1e5,13:12e4,14:14e4,15:165e3,16:195e3,17:225e3,18:265e3,19:305e3,20:355e3},Ma={1:2,2:2,3:2,4:2,5:3,6:3,7:3,8:3,9:4,10:4,11:4,12:4,13:5,14:5,15:5,16:5,17:6,18:6,19:6,20:6},Ps=["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"],Ae={adjectives:{techno:{bass:["Thumping","Pulsing","Driving","Hypnotic","Relentless","Mechanical","Deep"],treble:["Piercing","Synthetic","Digital","Sharp","Glittering","Laser"],mid:["Driving","Rhythmic","Flowing","Seamless","Kinetic"],quiet:["Minimal","Sparse","Subtle","Ambient","Whispered"],loud:["Pounding","Overwhelming","Massive","Explosive","Crushing"]},rock:{bass:["Heavy","Rumbling","Gritty","Raw","Crushing","Grinding"],treble:["Screaming","Wailing","Soaring","Piercing","Shrill"],mid:["Crunchy","Distorted","Overdriven","Gritty","Aggressive"],quiet:["Acoustic","Stripped","Bare","Intimate","Unplugged"],loud:["Thunderous","Explosive","Deafening","Roaring","Volcanic"]},metal:{bass:["Brutal","Crushing","Punishing","Merciless","Devastating","Obliterating"],treble:["Shredding","Screaming","Hellish","Demonic","Piercing"],mid:["Chugging","Grinding","Relentless","Aggressive","Furious"],quiet:["Doomed","Somber","Ominous","Foreboding","Dark"],loud:["Deafening","Apocalyptic","Cataclysmic","Earth-Shattering","Monolithic"]},ambient:{bass:["Deep","Vast","Profound","Oceanic","Cavernous","Abyssal"],treble:["Ethereal","Crystalline","Shimmering","Celestial","Gossamer"],mid:["Whispering","Drifting","Floating","Meandering","Breathing"],quiet:["Silent","Hushed","Meditative","Peaceful","Tranquil"],loud:["Swelling","Building","Crescendoing","Rising","Expanding"]},classical:{bass:["Grand","Stately","Majestic","Regal","Noble","Profound"],treble:["Soaring","Triumphant","Brilliant","Radiant","Gleaming"],mid:["Noble","Elegant","Refined","Graceful","Poised"],quiet:["Gentle","Delicate","Tender","Soft","Intimate"],loud:["Majestic","Powerful","Commanding","Heroic","Epic"]},jazz:{bass:["Smooth","Groovy","Walking","Swinging","Funky","Mellow"],treble:["Bright","Crisp","Brilliant","Clear","Sparkling"],mid:["Swinging","Syncopated","Bluesy","Soulful","Improvisational"],quiet:["Cool","Laid-back","Subtle","Understated","Intimate"],loud:["Big","Bold","Brassy","Energetic","Vibrant"]},"hip hop":{bass:["Bumping","Knocking","Booming","Heavy","Fat","Deep"],treble:["Sharp","Crisp","Cutting","Bright","Piercing"],mid:["Flowing","Smooth","Rhythmic","Funky","Grooving"],quiet:["Chill","Mellow","Lo-fi","Smooth","Laid-back"],loud:["Hype","Aggressive","Hard","Explosive","Banging"]},pop:{bass:["Bouncy","Punchy","Groovy","Catchy","Upbeat"],treble:["Sparkling","Bright","Shimmering","Glittering","Polished"],mid:["Catchy","Memorable","Infectious","Singable","Hooky"],quiet:["Soft","Gentle","Sweet","Tender","Intimate"],loud:["Anthemic","Soaring","Epic","Triumphant","Powerful"]},electronic:{bass:["Pulsing","Wobbling","Throbbing","Synthetic","Digital","Modulated"],treble:["Glitchy","Digital","Futuristic","Synthetic","Robotic"],mid:["Synthetic","Programmed","Sequenced","Processed","Modular"],quiet:["Atmospheric","Ambient","Minimal","Spacious","Ethereal"],loud:["Massive","Crushing","Drop-heavy","Explosive","Wall-of-sound"]},default:{bass:["Booming","Deep","Low","Resonant","Rumbling","Heavy"],treble:["Sharp","High","Piercing","Bright","Clear"],mid:["Resonant","Balanced","Full","Rich","Warm"],quiet:["Quiet","Soft","Gentle","Subtle","Hushed"],loud:["Loud","Powerful","Strong","Intense","Forceful"]}},descriptors:["Swift","Iron","Shadow","Mystic","Wild","Radiant","Grim","Noble","Ancient","Blazing"],classAspects:{Barbarian:["Warrior","Rage","Beast","Fury"],Bard:["Song","Voice","Muse","Melody"],Cleric:["Light","Faith","Devotion","Grace"],Druid:["Wild","Nature","Grove","Fang"],Fighter:["Blade","Shield","Defender","Champion"],Monk:["Fist","Spirit","Flow","Path"],Paladin:["Oath","Justice","Light","Defender"],Ranger:["Hunter","Arrow","Tracker","Scout"],Rogue:["Shadow","Blade","Whisper","Dagger"],Sorcerer:["Flame","Storm","Chaos","Power"],Warlock:["Pact","Shadow","Hex","Dark"],Wizard:["Arcane","Sage","Spellweaver","Mind"],Artificer:["Craft","Forge","Construct","Invention"]},prefixes:["Thunder","Shadow","Flame","Frost","Storm","Star","Blood","Soul","Moon","Sun"],suffixes:["Blessed","Forged","Wreathed","Touched","Born","Kissed","Bound","Marked","Sworn","Woven"],occupations:["smith","weaver","caller","keeper","herald","warden","seeker","walker","singer"],realms:["Eternal Stage","Forgotten Hall","Misty Vale","Iron Keep","Crystal Spire","Shadow Court","Golden Fields","Storm Peak","Ancient Grove","Silent Deep"],subtitlePrefixes:["of Stars","Eternal","Reborn","Unchained","Ascendant","the Fallen","Unbound","Rising","Triumphant","Immortal"]},Wr={athletics:"STR",acrobatics:"DEX",sleight_of_hand:"DEX",stealth:"DEX",arcana:"INT",history:"INT",investigation:"INT",nature:"INT",religion:"INT",animal_handling:"WIS",insight:"WIS",medicine:"WIS",perception:"WIS",survival:"WIS",deception:"CHA",intimidation:"CHA",performance:"CHA",persuasion:"CHA"};function la(s){if(s in Rt)return Rt[s];try{const e=R.getInstance(),t=`classSpellLists.${s}`,a=e.get(t);if(a&&a.length>0)return a[0]}catch{}}function Da(s,e){if(s in oa){const t=oa[s];if(t&&e in t)return t[e]}try{const i=R.getInstance().get("classSpellSlots");if(i&&Array.isArray(i)){const n=i.find(r=>r.class===s);if(n&&n.slots_by_level){const r=n.slots_by_level;if(e in r)return r[e]}}}catch{}}const zr=10,xs=50;class Ie{constructor(){this.allSpellsCache=null,this.levelCache=null,this.schoolCache=null,this.manager=R.getInstance()}static getInstance(){return Ie.instance||(Ie.instance=new Ie),Ie.instance}invalidateCache(){this.allSpellsCache=null,this.levelCache=null,this.schoolCache=null}getSpell(e){return this.getSpells().find(a=>a.id===e)}getSpells(){if(!this.allSpellsCache){const e=this.manager.get("spells");this.allSpellsCache=e}return this.allSpellsCache}getSpellsByLevel(e){if(!this.levelCache){this.levelCache=new Map;const t=this.getSpells();for(const a of t)this.levelCache.has(a.level)||this.levelCache.set(a.level,[]),this.levelCache.get(a.level).push(a)}return this.levelCache.get(e)||[]}getSpellsBySchool(e){if(!this.schoolCache){this.schoolCache=new Map;const t=this.getSpells();for(const a of t)this.schoolCache.has(a.school)||this.schoolCache.set(a.school,[]),this.schoolCache.get(a.school).push(a)}return this.schoolCache.get(e)||[]}getSpellsForClass(e){return this.getSpells().filter(a=>a.classes&&a.classes.includes(e))}getAvailableSpells(e){const t=this.getSpells(),a=[];for(const i of t)this.validatePrerequisites(i,e).valid&&a.push(i);return a}getClassSpellList(e){const t=`spells.${e}`;return this.manager.get(t)||[]}getSpellSlotsForClass(e,t){const a=Da(e,t);return a?Object.values(a).reduce((n,r)=>n+r,0):0}validatePrerequisites(e,t){return De.validateSpellPrerequisites(e.prerequisites,t)}validateSpell(e){return De.validateSpell(e)}hasSpell(e){return this.getSpell(e)!==void 0}getSpellCount(){return this.getSpells().length}getSpellsBySource(e){return this.getSpells().filter(t=>t.source===e)}getQueryStats(){const e=this.getSpells(),t=e.filter(o=>o.source==="default"),a=e.filter(o=>o.source==="custom"),i={};for(let o=0;o<=9;o++)i[o]=this.getSpellsByLevel(o).length;const n={Abjuration:this.getSpellsBySchool("Abjuration").length,Conjuration:this.getSpellsBySchool("Conjuration").length,Divination:this.getSpellsBySchool("Divination").length,Enchantment:this.getSpellsBySchool("Enchantment").length,Evocation:this.getSpellsBySchool("Evocation").length,Illusion:this.getSpellsBySchool("Illusion").length,Necromancy:this.getSpellsBySchool("Necromancy").length,Transmutation:this.getSpellsBySchool("Transmutation").length},r=new Set;for(const o of e)if(o.classes)for(const l of o.classes)r.add(l);return{totalSpells:e.length,defaultSpells:t.length,customSpells:a.length,spellsByLevel:i,spellsBySchool:n,classesWithSpells:r.size}}}function qr(){return Ie.getInstance()}class R{constructor(){this.defaultData=new Map,this.extensions=new Map,this.customWeights=new Map,this.imageOverrides=new Map}static getInstance(){return R.instance||(R.instance=new R),R.instance}initializeDefaults(e,t){this.defaultData.set(e,[...t]),this.invalidateRegistryCache(e)}initializeAllDefaults(e){for(const[t,a]of Object.entries(e))this.initializeDefaults(t,a)}register(e,t,a={}){const i=this.extensions.get(e),n=a.mode!==void 0?a.mode:i?.options.mode,{weights:r={},validate:o=!0}=a,l=["races","classes"];let c=t;if(l.includes(e)&&(c=t.map(d=>typeof d=="string"?d:d&&typeof d=="object"&&typeof d.name=="string"?d.name:d)),o){const d=this.validate(e,c);if(!d.valid)throw new Error(`Invalid items for category '${e}':
|
|
2
2
|
${d.errors?.join(`
|
|
3
3
|
`)}`)}if(e.startsWith("spells.")&&e!=="spells"){const d=this.get("spells"),h=new Set(d.map(m=>m.id)),p=[];if(t.length>0&&t[0]&&typeof t[0]=="object"&&"spells_by_level"in t[0])for(const m of t){const f=m;if(f.cantrips&&Array.isArray(f.cantrips))for(const v of f.cantrips)h.has(v)||p.push(v);if(f.spells_by_level){for(const v of Object.values(f.spells_by_level))if(Array.isArray(v))for(const w of v)h.has(w)||p.push(w)}}else{const m=t;for(const f of m)h.has(f)||p.push(f)}if(p.length>0)throw new Error(`Invalid spell IDs for ${e}: ${p.join(", ")}`)}if(n!=="replace"){const d=this.get(e),h=[];if(!["races","classes"].includes(e)){if(e.startsWith("appearance."))for(const g of c)typeof g=="string"&&d.includes(g)&&h.push(g);else for(const g of c)if(g&&typeof g=="object"){const m=g.name,f=g.id;(m||f)&&d.some(w=>!!(w&&typeof w=="object"&&(m&&w.name===m||f&&w.id===f)))&&h.push(m||f)}}if(h.length>0)throw new Error(`Duplicate items in category '${e}': ${h.join(", ")}. Each item must have a unique name or id.`)}const u=i&&n!=="replace"?[...i.items,...c]:[...c];if(this.extensions.set(e,{items:u,options:{mode:n==="replace"?"absolute":n,weights:r,validate:o},registeredAt:Date.now(),excludeDefaults:n==="replace"?!0:i?.excludeDefaults}),Object.keys(r).length>0){const d=this.customWeights.get(e)||{};this.customWeights.set(e,{...d,...r})}this.invalidateRegistryCache(e)}registerMultiple(e){for(const t of e)this.register(t.category,t.items,t.options)}get(e){const t=this.defaultData.get(e)||[],a=this.extensions.get(e);let i;return a?(a.options.mode,a.excludeDefaults?i=[...a.items]:i=[...t,...a.items]):i=[...t],this.isImageSupportedCategory(e)&&(i=this.applyImageOverrides(e,i)),i}isImageSupportedCategory(e){return["spells","skills","classFeatures","racialTraits","equipment","races.data","classes.data"].includes(e)}applyImageOverrides(e,t){const a=this.imageOverrides.get(e);if(!a||a.size===0)return t;const i=this.getDefaultIdentifierKey(e);return t.map(n=>{const r=n[i],o=a.get(r);return o?{...n,...o.icon!==void 0&&{icon:o.icon},...o.image!==void 0&&{image:o.image}}:n})}getDefaults(e){return[...this.defaultData.get(e)||[]]}getCustom(e){const t=this.extensions.get(e);return t?[...t.items]:[]}setWeights(e,t){this.customWeights.set(e,{...t})}getWeights(e){const t=this.getDefaultWeights(e),a=this.customWeights.get(e)||{},i=this.getCustom(e),n={};for(const r of i){const o=typeof r=="string"?r:r.name;o&&!(o in t)&&!(o in a)&&(n[o]=1)}return{...t,...n,...a}}getDefaultWeights(e){const t=this.defaultData.get(e)||[],a={};for(const i of t){const n=typeof i=="string"?i:i.name;n&&(a[n]=1)}return a}hasCustomData(e){return this.extensions.has(e)}setMode(e,t){const a=this.extensions.get(e);a?a.options.mode=t:this.extensions.set(e,{items:[],options:{mode:t},registeredAt:Date.now()})}getMode(e){return this.extensions.get(e)?.options.mode}getCurrentOptions(e){return this.extensions.get(e)?.options}validate(e,t){const a=[];if(!Array.isArray(t))return a.push("Items must be an array"),{valid:!1,errors:a};for(let i=0;i<t.length;i++){const n=t[i],r=this.validateItem(e,n,i);a.push(...r)}return a.length>0?{valid:!1,errors:a}:{valid:!0}}validateItem(e,t,a){const i=[],n=`Item ${a}:`;if(t==null)return i.push(`${n} Item is null or undefined`),i;if(e==="equipment"){const r=Dt.validateEquipment(t);r.valid||i.push(...(r.errors||[]).map(o=>`${n} ${o}`))}else if(e==="spells"||e.startsWith("spells.")){if(!(t&&typeof t=="object"&&"spells_by_level"in t)){const o=De.validateSpell(t);o.valid||i.push(...o.errors.map(l=>`${n} ${l}`))}}else if(e==="races"){const r=_r;if(r.includes(t))return i;const o=this.get("races");if(Array.isArray(o)&&o.includes(t))return i;const l=this.get("races.data");if(Array.isArray(l)&&l.map(u=>u.race).filter(Boolean).includes(t))return i;i.push(`${n} Invalid race "${t}". Must be one of: ${r.join(", ")} or register custom race via 'races.data' first.`)}else if(e==="races.data"){if((!t.race||typeof t.race!="string")&&i.push(`${n} Race data must have a valid 'race' property (string)`),(typeof t.speed!="number"||t.speed<=0)&&i.push(`${n} Race data must have a valid 'speed' property (positive number)`),Array.isArray(t.traits)||i.push(`${n} Race data must have a 'traits' property (array of strings)`),t.ability_bonuses)if(typeof t.ability_bonuses!="object"||Array.isArray(t.ability_bonuses))i.push(`${n} Race 'ability_bonuses' must be a record of ability -> number`);else{const o=["STR","DEX","CON","INT","WIS","CHA"];for(const[l,c]of Object.entries(t.ability_bonuses))o.includes(l)||i.push(`${n} Invalid ability "${l}" in ability_bonuses`),typeof c!="number"&&i.push(`${n} Ability bonus for "${l}" must be a number`)}t.subraces!==void 0&&!Array.isArray(t.subraces)&&i.push(`${n} Race 'subraces' must be an array of strings (if provided)`);const r=Be({icon:t.icon,image:t.image});i.push(...r.map(o=>`${n} Race ${o}`))}else if(e==="classes"){const r=t,o=Re.includes(r),c=this.get("classes")?.includes(r),d=this.get("classes.data")?.some(h=>h.name===r);!o&&!c&&!d&&i.push(`${n} Invalid class (must be one of: ${Re.join(", ")} or a custom class registered via 'classes.data')`)}else if(e==="classes.data"){(!t.name||typeof t.name!="string")&&i.push(`${n} Class data must have a valid 'name' property (string)`);const r=["STR","DEX","CON","INT","WIS","CHA"];if((!t.primary_ability||!r.includes(t.primary_ability))&&i.push(`${n} Class data must have a valid 'primary_ability' (one of: STR, DEX, CON, INT, WIS, CHA)`),(typeof t.hit_die!="number"||t.hit_die<=0)&&i.push(`${n} Class data must have a valid 'hit_die' property (positive number)`),!Array.isArray(t.saving_throws))i.push(`${n} Class data must have a 'saving_throws' property (array of abilities)`);else for(const l of t.saving_throws)r.includes(l)||i.push(`${n} Invalid saving throw "${l}" in class data`);if(typeof t.is_spellcaster!="boolean"&&i.push(`${n} Class data must have an 'is_spellcaster' property (boolean)`),(typeof t.skill_count!="number"||t.skill_count<0)&&i.push(`${n} Class data must have a valid 'skill_count' property (non-negative number)`),Array.isArray(t.available_skills)||i.push(`${n} Class data must have an 'available_skills' property (array of skill IDs)`),typeof t.has_expertise!="boolean"&&i.push(`${n} Class data must have a 'has_expertise' property (boolean)`),t.expertise_count!==void 0&&(typeof t.expertise_count!="number"||t.expertise_count<0)&&i.push(`${n} Class 'expertise_count' must be a non-negative number (if provided)`),t.baseClass!==void 0)if(typeof t.baseClass!="string")i.push(`${n} Class 'baseClass' must be a string (if provided)`);else{const l=Re.includes(t.baseClass),u=this.get("classes.data")?.some(d=>d.name===t.baseClass);!l&&!u&&i.push(`${n} Class 'baseClass' must be a valid default class (one of: ${Re.join(", ")}) or a registered custom class`)}t.audio_preferences!==void 0&&typeof t.audio_preferences!="object"&&i.push(`${n} Class 'audio_preferences' must be an object (if provided)`);const o=Be({icon:t.icon,image:t.image});i.push(...o.map(l=>`${n} Class ${l}`))}else if(e.startsWith("appearance."))typeof t!="string"&&i.push(`${n} Appearance options must be strings`);else if(e==="classFeatures"||e.startsWith("classFeatures.")){const r=this.get("classes")??[],o=Fe.validateClassFeature(t,{customClasses:r});o.valid||i.push(...o.errors.map(l=>`${n} ${l}`))}else if(e==="racialTraits"||e.startsWith("racialTraits.")){const r=this.get("races")??[],l=this.get("races.data")?.map(d=>d.race).filter(Boolean)??[],c=[...r,...l],u=Fe.validateRacialTrait(t,{customRaces:c});u.valid||i.push(...u.errors.map(d=>`${n} ${d}`))}else if(e==="skills"||e.startsWith("skills.")){const r=we.validateSkill(t);r.valid||i.push(...r.errors.map(o=>`${n} ${o}`))}else(e==="skillLists"||e.startsWith("skillLists."))&&((!t.class||typeof t.class!="string")&&i.push(`${n} Skill list must have a valid 'class'`),(typeof t.skillCount!="number"||t.skillCount<0)&&i.push(`${n} Invalid 'skillCount' (must be non-negative number)`),Array.isArray(t.availableSkills)||i.push(`${n} Invalid 'availableSkills' (must be an array of skill IDs)`),t.expertiseCount!==void 0&&(typeof t.expertiseCount!="number"||t.expertiseCount<0)&&i.push(`${n} Invalid 'expertiseCount' (must be non-negative number)`));return i}reset(e){this.extensions.delete(e),this.customWeights.delete(e),this.invalidateRegistryCache(e)}resetAll(){this.extensions.clear(),this.customWeights.clear(),Ie.getInstance().invalidateCache(),pe.getInstance().invalidateCache(),re.getInstance().invalidateCache()}getRegistryForCategory(e){return e==="spells"||e.startsWith("spells.")||e==="classSpellLists"||e.startsWith("classSpellLists.")?"spell":e==="skills"||e.startsWith("skills.")||e==="skillLists"||e.startsWith("skillLists.")?"skill":e==="classFeatures"||e.startsWith("classFeatures.")||e==="racialTraits"||e.startsWith("racialTraits.")?"feature":null}invalidateRegistryCache(e){const t=this.getRegistryForCategory(e);t==="spell"?Ie.getInstance().invalidateCache():t==="skill"?pe.getInstance().invalidateCache():t==="feature"&&re.getInstance().invalidateCache()}getInfo(e){if(e){const a=this.extensions.get(e),i=this.defaultData.get(e)||[];return{hasCustomData:this.hasCustomData(e),defaultCount:i.length,customCount:a?.items.length||0,totalCount:this.get(e).length,mode:this.getMode(e),weights:this.getWeights(e),registeredAt:a?.registeredAt}}const t={};for(const a of this.defaultData.keys())t[a]=this.getInfo(a);return t}exportCustomData(){const e={};for(const[a,i]of this.extensions.entries())e[a]={items:i.items,options:i.options,registeredAt:i.registeredAt};const t={};for(const[a,i]of this.customWeights.entries())t[a]=i;return{extensions:e,weights:t}}exportCustomDataForCategory(e){const t=this.extensions.get(e);return t?[...t.items]:[]}getRegisteredCategories(){return Array.from(this.defaultData.keys())}getAllFromPrefix(e){const t=[],a=new Set,i=r=>{for(const o of r){const l=o.id||JSON.stringify(o);a.has(l)||(a.add(l),t.push(o))}},n=new Set;for(const r of this.defaultData.keys())(r===e||r.startsWith(e+"."))&&n.add(r);for(const r of this.extensions.keys())(r===e||r.startsWith(e+"."))&&n.add(r);for(const r of n)i(this.get(r));return t}batchAddIcons(e,t,a){const i=a??this.getDefaultIdentifierKey(e),n=Object.entries(t).map(([u,d])=>[d,u]),r=this.validateImageFields(n);if(r.length>0)throw new Error(`Invalid icon URLs for category '${e}':
|
|
4
4
|
${r.map(u=>` - ${u}`).join(`
|
|
@@ -8,9 +8,9 @@ ${r.map(u=>` - ${u}`).join(`
|
|
|
8
8
|
${n.map(d=>` - ${d}`).join(`
|
|
9
9
|
`)}`);const r=this.get(e),o=this.getDefaultIdentifierKey(e),l=Date.now();let c=0;this.imageOverrides.has(e)||this.imageOverrides.set(e,new Map);const u=this.imageOverrides.get(e);for(const d of r)if(t(d)){const h=d[o],p=u.get(h),g={identifier:h,icon:a.icon!==void 0?a.icon:p?.icon,image:a.image!==void 0?a.image:p?.image,appliedAt:l};u.set(h,g),c++}return this.invalidateRegistryCache(e),c}batchByCategory(e,t,a){const i=[];for(const[d,h]of Object.entries(a))typeof h=="string"?i.push([h,d]):(h.icon!==void 0&&i.push([h.icon,`${d}.icon`]),h.image!==void 0&&i.push([h.image,`${d}.image`]));const n=this.validateImageFields(i);if(n.length>0)throw new Error(`Invalid URLs in valueMap for category '${e}':
|
|
10
10
|
${n.map(d=>` - ${d}`).join(`
|
|
11
|
-
`)}`);const r=this.get(e),o=this.getDefaultIdentifierKey(e),l=Date.now();let c=0;this.imageOverrides.has(e)||this.imageOverrides.set(e,new Map);const u=this.imageOverrides.get(e);for(const d of r){const h=String(d[t]??""),p=a[h];if(p!==void 0){const g=d[o],m=u.get(g);let f,v;typeof p=="string"?(f=p,v=m?.image):(f=p.icon!==void 0?p.icon:m?.icon,v=p.image!==void 0?p.image:m?.image);const w={identifier:g,icon:f,image:v,appliedAt:l};u.set(g,w),c++}}return this.invalidateRegistryCache(e),c}getImageOverrides(){const e=new Map;for(const[t,a]of this.imageOverrides.entries())e.set(t,Array.from(a.values()));return e}getImageOverridesForCategory(e){const t=this.imageOverrides.get(e);return t?Array.from(t.values()):[]}restoreImageOverrides(e,t){if(!t||t.length===0)return;this.imageOverrides.has(e)||this.imageOverrides.set(e,new Map);const a=this.imageOverrides.get(e);for(const i of t)a.set(i.identifier,i);this.invalidateRegistryCache(e)}clearImageOverrides(e){this.imageOverrides.delete(e),this.invalidateRegistryCache(e)}clearAllImageOverrides(){this.imageOverrides.clear();const e=["spells","skills","classFeatures","racialTraits","equipment","races.data","classes.data"];for(const t of e)this.invalidateRegistryCache(t)}getDefaultIdentifierKey(e){return"name"}validateImageField(e,t){return Es(e)?null:`${t}: ${e} (URLs must start with: http://, https://, /, or assets/)`}validateImageFields(e){const t=[];for(const[a,i]of e){const n=this.validateImageField(a,i);n!==null&&t.push(n)}return t}}const Ur=Object.freeze(Object.defineProperty({__proto__:null,ExtensionManager:R},Symbol.toStringTag,{value:"Module"})),Ns={Human:{ability_bonuses:{STR:1,DEX:1,CON:1,INT:1,WIS:1,CHA:1},speed:30,traits:["Versatile","Extra Language"],subraces:["Calishite","Chondathan","Damaran","Illuskan","Mulan","Rashemi","Shou","Tethyrian","Turami"],description:"Humans are the most adaptable and ambitious people among the common races. They have widely varying tastes, morals, and customs in the many different lands they call home. Their drive and their innovative spirit help them push boundaries and explore new frontiers."},Elf:{ability_bonuses:{DEX:2},speed:30,traits:["Darkvision","Keen Senses","Fey Ancestry","Trance"],subraces:["High Elf","Wood Elf","Dark Elf (Drow)"],description:"Elves are a magical people of otherworldly grace, living in the world but apart from it. They possess an innate connection to the Feywild and the natural world, with lifespans that measure in centuries. Elves value art, music, and nature, and their society emphasizes individual freedom and creative expression."},Dwarf:{ability_bonuses:{CON:2},speed:25,traits:["Darkvision","Dwarven Resilience","Stonecunning"],subraces:["Hill Dwarf","Mountain Dwarf"],description:"Bold and hardy, dwarves are known as skilled warriors, miners, and workers of stone and metal. They stand well under 5 feet tall but are so broad and compact that they can weigh as much as a human. Dwarves value clan, tradition, and craftsmanship, often dwelling in mountains or underground kingdoms."},Halfling:{ability_bonuses:{DEX:2},speed:25,traits:["Lucky","Brave","Halfling Nimbleness"],subraces:["Lightfoot","Stout"],description:"The diminutive halflings survive in a world full of larger creatures by avoiding notice or, barring that, avoiding offense. They are comfortable and lucky, taking what life brings with good cheer. Standing about 3 feet tall, they appear relatively harmless and so have managed to survive for centuries in the shadow of empires and on the edges of wars."},Dragonborn:{ability_bonuses:{STR:2,CHA:1},speed:30,traits:["Draconic Ancestry","Breath Weapon","Damage Resistance"],subraces:["Chromatic","Metallic","Gem"],description:"Dragonborn look very much like dragons standing erect in humanoid form, though they lack wings or tails. Proud and noble, they carry the blood of ancient dragons within them, granting them a breath weapon and resistance to elemental damage. They value honor and clan above all else."},Gnome:{ability_bonuses:{INT:2},speed:25,traits:["Darkvision","Gnome Cunning"],subraces:["Forest Gnome","Rock Gnome"],description:"Gnomes are inventive and curious, with an innate connection to the Illusory and magical arts. Standing about 3 to 3.5 feet tall, they approach problems with enthusiasm and optimism. Their society prizes humor, clever inventions, and arcane scholarship, with many gnomes becoming accomplished wizards, engineers, or pranksters."},"Half-Elf":{ability_bonuses:{CHA:2},speed:30,traits:["Darkvision","Fey Ancestry","Skill Versatility"],description:"Half-elves combine the best qualities of their elven and human parents. They share the elves' grace and curiosity, along with the humans' drive and versatility. As natural diplomats and charismatic leaders, half-elves can fit into virtually any society, often serving as bridges between cultures."},"Half-Orc":{ability_bonuses:{STR:2,CON:1},speed:30,traits:["Darkvision","Relentless Endurance","Savage Attacks"],description:"Half-orcs inherit the physical prowess and cunning of their orcish ancestors, combined with the adaptability and ambition of their human heritage. They stand taller than most humans, with grayish skin, tusks, and faces that combine bestial and humanoid features. Half-orcs are often outcasts but prove themselves as ferocious warriors and loyal companions."},Tiefling:{ability_bonuses:{CHA:2,INT:1},speed:30,traits:["Darkvision","Hellish Resistance","Infernal Legacy"],description:"Tieflings are humanoids with infernal blood, often bearing horns, tails, and other fiendish features. Their ancestry grants them resistance to fire and innate magical abilities. Though often feared and distrusted due to their appearance, tieflings are as varied in morality and ambition as any other race, with many becoming powerful warlocks, sorcerers, or charismatic leaders."}},Gr=Ns,$s=Object.entries(Ns).map(([s,e])=>({race:s,...e}));let Xt=null,Kt=null;async function jr(s){try{Xt||(Kt||(Kt=Promise.resolve().then(()=>Ur)),Xt=(await Kt).ExtensionManager);const t=Xt.getInstance().get("races.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.race===s);if(a)return a}}catch{}}function zt(s){try{const t=R.getInstance().get("races.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.race===s);if(a)return a}}catch{}}class re{constructor(){this.allClassFeaturesCache=null,this.classFeaturesIndex=null,this.allRacialTraitsCache=null,this.racialTraitsIndex=null,this.manager=R.getInstance()}static getInstance(){return re.instance||(re.instance=new re),re.instance}invalidateCache(){this.allClassFeaturesCache=null,this.classFeaturesIndex=null,this.allRacialTraitsCache=null,this.racialTraitsIndex=null}getAllClassFeaturesArray(){if(!this.allClassFeaturesCache){const e=this.manager.get("classFeatures");this.allClassFeaturesCache=e}return this.allClassFeaturesCache}getAllRacialTraitsArray(){if(!this.allRacialTraitsCache){const e=this.manager.getAllFromPrefix("racialTraits");this.allRacialTraitsCache=e}return this.allRacialTraitsCache}getClassFeatures(e,t){return this.getAllClassFeaturesArray().filter(i=>i.class===e&&i.level<=t)}getFeaturesForLevel(e,t){return this.getAllClassFeaturesArray().filter(i=>i.class===e&&i.level===t)}getClassFeaturesForLevel(e,t){return this.getFeaturesForLevel(e,t)}getClassFeatureById(e){return this.getAllClassFeaturesArray().find(a=>a.id===e)}getAllClassFeatures(){if(!this.classFeaturesIndex){this.classFeaturesIndex=new Map;const e=this.getAllClassFeaturesArray();for(const t of e)this.classFeaturesIndex.has(t.class)||this.classFeaturesIndex.set(t.class,[]),this.classFeaturesIndex.get(t.class).push(t)}return new Map(this.classFeaturesIndex)}getRacialTraits(e){return this.getAllRacialTraitsArray().filter(a=>a.race===e)}getBaseRacialTraits(e){return this.getAllRacialTraitsArray().filter(a=>a.race===e&&!a.subrace)}getRacialTraitsForSubrace(e,t){return this.getAllRacialTraitsArray().filter(i=>i.race===e&&(!i.subrace||i.subrace===t))}getSubraceTraits(e,t){return this.getAllRacialTraitsArray().filter(i=>i.race===e&&i.subrace===t)}getAvailableSubraces(e){const t=zt(e);if(t?.subraces&&t.subraces.length>0)return t.subraces;const a=this.getAllRacialTraitsArray(),i=new Set;for(const n of a)n.race===e&&n.subrace&&i.add(n.subrace);return Array.from(i).sort()}getRaceForSubrace(e){const t=this.getAllRacialTraitsArray();for(const a of t)if(a.subrace===e)return a.race}getRacialTraitById(e){return this.getAllRacialTraitsArray().find(a=>a.id===e)}getAllRacialTraits(){if(!this.racialTraitsIndex){this.racialTraitsIndex=new Map;const e=this.getAllRacialTraitsArray();for(const t of e)this.racialTraitsIndex.has(t.race)||this.racialTraitsIndex.set(t.race,[]),this.racialTraitsIndex.get(t.race).push(t)}return new Map(this.racialTraitsIndex)}validatePrerequisites(e,t){const a=[];if(!e.prerequisites)return{valid:!0};const i=e.prerequisites;if(i.level!==void 0&&t.level<i.level&&a.push(`Requires level ${i.level} (current: ${t.level})`),i.abilities)for(const[n,r]of Object.entries(i.abilities)){const o=t.ability_scores[n];(o===void 0||o<r)&&a.push(`Requires ${n} ${r} (current: ${o??0})`)}if(i.class!==void 0&&t.class!==i.class&&a.push(`Requires class ${i.class}`),i.race!==void 0&&t.race!==i.race&&a.push(`Requires race ${i.race} (current: ${t.race})`),i.subrace!==void 0&&(!t.subrace||t.subrace!==i.subrace)&&a.push(`Requires subrace ${i.subrace} (current: ${t.subrace||"none"})`),i.skills&&i.skills.length>0)for(const n of i.skills){const r=t.skills[n];r!=="proficient"&&r!=="expertise"&&a.push(`Requires proficiency in ${n}`)}if(i.spells&&i.spells.length>0){const n=t.spells?.known_spells||[],r=t.spells?.cantrips||[],o=[...n,...r];for(const l of i.spells)o.includes(l)||a.push(`Requires spell: ${l}`)}if(i.features&&i.features.length>0){const n=this.getCharacterFeatureIds(t);for(const r of i.features)if(!n.includes(r)){const l=this.getClassFeatureById(r)?.name||r;a.push(`Requires feature: ${l}`)}}return{valid:a.length===0,errors:a.length>0?a:void 0,unmet:a.length>0?a:void 0}}validateFeaturePrerequisites(e,t){return this.validatePrerequisites(e,t)}validateTraitPrerequisites(e,t){return this.validatePrerequisites(e,t)}getCharacterFeatureIds(e){return e.class_features||[]}canGainFeature(e,t){return this.validatePrerequisites(e,t).valid}meetsPrerequisites(e,t){return this.canGainFeature(e,t)}getRegisteredClasses(){const e=this.getAllClassFeaturesArray(),t=new Set;for(const a of e)t.add(a.class);return Array.from(t)}getRegisteredRaces(){const e=this.getAllRacialTraitsArray(),t=new Set;for(const a of e)t.add(a.race);return Array.from(t)}getQueryStats(){const e=this.getAllClassFeaturesArray(),t=new Set;for(const n of e)t.add(n.class);const a=this.getAllRacialTraitsArray(),i=new Set;for(const n of a)i.add(n.race);return{totalClassFeatures:e.length,totalRacialTraits:a.length,classesWithFeatures:t.size,racesWithTraits:i.size}}clearQueryCache(){this.manager.reset("classFeatures"),this.manager.reset("racialTraits"),this.invalidateCache()}isInitialized(){return this.manager.hasCustomData("racialTraits")||this.manager.getDefaults("racialTraits").length>0}static getEquipmentFeatures(e){const t=re.getInstance(),a=[],i=t.getAllClassFeaturesArray();for(const n of i)(n.tags?.includes("equipment")||n.tags?.includes("item")||n.source==="custom")&&a.push(n);return a}static isValidEquipmentFeature(e){return!!re.getInstance().getClassFeatureById(e)}exportRacialTraits(){const e={},t=this.getAllRacialTraits();for(const[a,i]of t.entries())e[a]=i;return e}}function Vr(){return re.getInstance()}const Yr={greataxe:"Greataxe",longsword:"Longsword","brute-mace":"Mace",handaxe:"Handaxe",longbow:"Longbow","light-crossbow":"Light Crossbow",shortsword:"Shortsword",dagger:"Dagger",quarterstaff:"Quarterstaff","support-mace":"Mace","support-dagger":"Dagger","leather-armor":"Leather Armor","scale-mail":"Scale Mail","chain-mail":"Chain Mail","plate-armor":"Plate Armor",shield:"Shield"},qe=[{id:"greataxe",name:"Greataxe",type:"weapon",archetypes:["brute"],rarities:["uncommon","elite","boss"],damage:"1d12",properties:["two-handed"]},{id:"longsword",name:"Longsword",type:"weapon",archetypes:["brute"],rarities:["common","uncommon","elite","boss"],damage:"1d8",properties:["versatile"]},{id:"brute-mace",name:"Mace",type:"weapon",archetypes:["brute"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile"]},{id:"handaxe",name:"Handaxe",type:"weapon",archetypes:["brute"],rarities:["common","uncommon"],damage:"1d6",properties:["light","thrown"]},{id:"longbow",name:"Longbow",type:"weapon",archetypes:["archer"],rarities:["uncommon","elite","boss"],damage:"1d8",properties:["ammunition","two-handed","heavy"]},{id:"light-crossbow",name:"Light Crossbow",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite"],damage:"1d8",properties:["ammunition","two-handed","loading"]},{id:"shortsword",name:"Shortsword",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["finesse","light"]},{id:"dagger",name:"Dagger",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite","boss"],damage:"1d4",properties:["finesse","light","thrown"]},{id:"quarterstaff",name:"Quarterstaff",type:"weapon",archetypes:["support"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile","two-handed"]},{id:"support-mace",name:"Mace",type:"weapon",archetypes:["support"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile"]},{id:"support-dagger",name:"Dagger",type:"weapon",archetypes:["support"],rarities:["common","uncommon"],damage:"1d4",properties:["finesse","light","thrown"]},{id:"leather-armor",name:"Leather Armor",type:"armor",archetypes:["brute","archer","support"],rarities:["common","uncommon","elite","boss"],acBonus:11},{id:"scale-mail",name:"Scale Mail",type:"armor",archetypes:["brute","support"],rarities:["uncommon","elite","boss"],acBonus:14},{id:"chain-mail",name:"Chain Mail",type:"armor",archetypes:["brute","support"],rarities:["elite","boss"],acBonus:16},{id:"plate-armor",name:"Plate Armor",type:"armor",archetypes:["brute"],rarities:["boss"],acBonus:18},{id:"shield",name:"Shield",type:"shield",archetypes:["brute","support"],rarities:["uncommon","elite","boss"],acBonus:2}];function Ra(s,e){return qe.filter(t=>t.archetypes.includes(s)&&t.rarities.includes(e))}function Zr(s,e){return Ra(s,e).filter(t=>t.type==="weapon")}function Xr(s,e){return Ra(s,e).filter(t=>t.type==="armor")}function Ii(s,e){return Ra(s,e).filter(t=>t.type==="shield")}const fe=class fe{static generate(e){const{archetype:t,rarity:a,seed:i,includeShield:n=!0}=e,r=new Se(i),o={};if(o.weapon=fe.selectWeapon(t,a,r),o.armor=fe.selectArmor(t,a,r),n&&fe.canUseShield(t)&&Ii(t,a).length>0){const c=fe.SHIELD_CHANCE[a];r.random()<c&&(o.shield=fe.selectShield(t,a))}return o}static selectWeapon(e,t,a){const i=Zr(e,t);if(i.length===0)return qe.find(o=>o.id==="dagger");const n=fe.WEAPON_PRIORITY[e][t],r=i.filter(o=>n.includes(o.id));return r.length>0&&a.random()<.7?a.randomChoice(r):a.randomChoice(i)}static selectArmor(e,t,a){const i=Xr(e,t);if(i.length===0)return qe.find(o=>o.id==="leather-armor");const n=fe.ARMOR_PRIORITY[e][t],r=i.filter(o=>n.includes(o.id));return r.length>0&&a.random()<.8?a.randomChoice(r):a.randomChoice(i)}static selectShield(e,t){const a=Ii(e,t);return a.length===0?qe.find(i=>i.id==="shield"):a[0]}static canUseShield(e){return e!=="archer"}static getEquipmentName(e){return Yr[e]||e}static getAllTemplates(){return[...qe]}static getTemplateById(e){return qe.find(t=>t.id===e)}};fe.WEAPON_PRIORITY={brute:{common:["handaxe","brute-mace"],uncommon:["longsword","brute-mace"],elite:["greataxe","longsword"],boss:["greataxe","longsword"]},archer:{common:["light-crossbow","dagger"],uncommon:["longbow","shortsword"],elite:["longbow","shortsword"],boss:["longbow","shortsword"]},support:{common:["support-dagger","quarterstaff"],uncommon:["quarterstaff","support-mace"],elite:["quarterstaff","support-mace"],boss:["quarterstaff","support-mace"]}},fe.ARMOR_PRIORITY={brute:{common:["leather-armor"],uncommon:["scale-mail","leather-armor"],elite:["chain-mail","scale-mail"],boss:["plate-armor","chain-mail"]},archer:{common:["leather-armor"],uncommon:["leather-armor"],elite:["leather-armor"],boss:["leather-armor"]},support:{common:["leather-armor"],uncommon:["scale-mail","leather-armor"],elite:["chain-mail","scale-mail"],boss:["chain-mail","scale-mail"]}},fe.SHIELD_CHANCE={common:.25,uncommon:.5,elite:.75,boss:1};let Ge=fe;const vt={0:{},.125:{},.25:{},.5:{},1:{1:3},2:{1:4},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3},8:{1:4,2:3,3:3,4:1},9:{1:4,2:3,3:3,4:2},10:{1:4,2:3,3:3,4:2}},bt={support:{archetype:"support",cantrips:[{id:"support_sacred_flame",name:"Sacred Flame",level:0,school:"evocation",effect:"A flame of divine brightness springs from your hand. Deals radiant damage to one target.",damage:"1d8",damageType:"radiant",rangeFeet:60,tags:["damage","ranged"]},{id:"support_guidance",name:"Guidance",level:0,school:"divination",effect:"You touch one willing creature. Once before the spell ends, the target can roll a d4 and add the number rolled to one ability check of its choice.",rangeFeet:30,concentration:!1,tags:["buff","utility"]},{id:"support_resistance",name:"Resistance",level:0,school:"abjuration",effect:"You touch one willing creature. The target gains resistance to one damage type of your choice until the spell ends.",rangeFeet:30,concentration:!0,tags:["buff","defense"]}],level1:[{id:"support_bless",name:"Bless",level:1,school:"enchantment",effect:"You bless up to three creatures of your choice within range. Whenever a target makes an attack roll or saving throw before the spell ends, the target can roll a d4 and add the number rolled to the d20.",rangeFeet:30,concentration:!0,tags:["buff","ally","multi-target"]},{id:"support_bane",name:"Bane",level:1,school:"enchantment",effect:"Up to three creatures of your choice that you can see within range must make a Wisdom saving throw. On a failed save, the target has disadvantage on attack rolls for the duration.",save:"WIS",rangeFeet:30,concentration:!0,tags:["debuff","control"]},{id:"support_cure_wounds",name:"Cure Wounds",level:1,school:"evocation",effect:"A creature you touch regains a number of hit points equal to 1d8 + your spellcasting ability modifier.",rangeFeet:30,tags:["healing","ally"]},{id:"support_healing_word",name:"Healing Word",level:1,school:"evocation",effect:"A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier.",rangeFeet:60,tags:["healing","ally","bonus-action"]},{id:"support_command",name:"Command",level:1,school:"enchantment",effect:"You speak a one-word command to a creature you can see within range. The target must succeed on a Wisdom save or follow the command on its next turn.",save:"WIS",rangeFeet:60,tags:["control","debuff"]}],level2:[{id:"support_aid",name:"Aid",level:2,school:"evocation",effect:"Your spell bolsters your allies with toughness and resolve. Up to three creatures gain temporary hit points and advantage on certain ability checks.",rangeFeet:30,tags:["buff","ally","multi-target"]},{id:"support_lesser_restoration",name:"Lesser Restoration",level:2,school:"abjuration",effect:"You touch a creature and can end either one disease or one condition afflicting it. The condition can be blinded, deafened, paralyzed, or poisoned.",rangeFeet:30,tags:["healing","ally","utility"]},{id:"support_spiritual_weapon",name:"Spiritual Weapon",level:2,school:"evocation",effect:"You create a spectral weapon within range. It can be used to attack, dealing force damage on a hit.",damage:"1d8 + 1",damageType:"force",rangeFeet:60,concentration:!1,tags:["summon","damage"]},{id:"support_shatter",name:"Shatter",level:2,school:"evocation",effect:"A sudden loud ringing sound, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution saving throw.",damage:"3d8",damageType:"thunder",save:"CON",rangeFeet:60,tags:["damage","aoe"]}],level3:[{id:"support_spirit_guardians",name:"Spirit Guardians",level:3,school:"conjuration",effect:"You call forth spirits to protect you. The spirits are intangible, spectral guardians that flank you. Each creature of your choice that you can see within range must succeed on a Wisdom saving throw or be restrained.",damage:"2d8",damageType:"radiant",save:"WIS",rangeFeet:30,concentration:!0,tags:["defense","control","aoe"]},{id:"support_revivify",name:"Revivify",level:3,school:"conjuration",effect:"You touch a creature that has died within the last minute. That creature returns to life with 1 hit point.",rangeFeet:30,tags:["healing","ally","utility"]},{id:"support_mass_healing_word",name:"Mass Healing Word",level:3,school:"evocation",effect:"Up to six creatures of your choice that you can see within range regain hit points equal to 1d4 + your spellcasting ability modifier.",rangeFeet:60,tags:["healing","ally","multi-target"]}]},archer:{archetype:"archer",cantrips:[{id:"archer_ray_of_frost",name:"Ray of Frost",level:0,school:"evocation",effect:"A frigid beam of blue-white light streaks toward a creature within range. On a hit, the target takes cold damage and its speed is reduced by 10 feet until the start of your next turn.",damage:"1d8",damageType:"cold",rangeFeet:60,tags:["damage","control","ranged"]},{id:"archer_shocking_grasp",name:"Shocking Grasp",level:0,school:"evocation",effect:"Lightning springs from your hand to deliver a shock to a creature you try to touch. The target takes lightning damage and cannot take reactions until its next turn.",damage:"1d8",damageType:"lightning",rangeFeet:30,tags:["damage","melee","control"]},{id:"archer_true_strike",name:"True Strike",level:0,school:"divination",effect:"You extend your hand and point a finger at a target in range. Your magic grants you a brief insight into the target's defenses. On your next turn, you gain advantage on your first attack roll against the target.",rangeFeet:30,tags:["buff","self"]}],level1:[{id:"archer_misty_step",name:"Misty Step",level:1,school:"conjuration",effect:"You briefly vanish from existence and teleport to a nearby space. During your next turn, you gain advantage on the first melee attack you make against an enemy.",rangeFeet:90,tags:["mobility","escape","buff"]},{id:"archer_hold_person",name:"Hold Person",level:1,school:"enchantment",effect:"Choose a humanoid that you can see within range. The target must succeed on a Wisdom saving throw or be paralyzed for the duration.",save:"WIS",rangeFeet:60,concentration:!0,tags:["control","debuff"]},{id:"archer_ray_of_sickness",name:"Ray of Sickness",level:1,school:"necromancy",effect:"A ray of sickening greenish energy lashes out toward a creature within range. The target must make a Constitution saving throw or take poison damage and have disadvantage on its next attack.",damage:"2d8",damageType:"poison",save:"CON",rangeFeet:60,tags:["damage","debuff"]},{id:"archer_thunderwave",name:"Thunderwave",level:1,school:"evocation",effect:"A wave of thunderous force sweeps out from you. Each creature in a 15-foot cube must make a Constitution saving throw or take thunder damage and be pushed 10 feet away.",damage:"2d8",damageType:"thunder",save:"CON",rangeFeet:30,tags:["damage","aoe","control"]}],level2:[{id:"archer_web",name:"Web",level:2,school:"conjuration",effect:"You cast a web of thick, sticky strands at a point within range. Creatures in the area must make a Dexterity saving throw or be restrained.",save:"DEX",rangeFeet:60,concentration:!1,tags:["control","aoe","restrain"]},{id:"archer_invisibility",name:"Invisibility",level:2,school:"illusion",effect:"A creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person.",rangeFeet:30,concentration:!0,tags:["stealth","defense","utility"]},{id:"archer_melfs_acid_arrow",name:"Melf's Acid Arrow",level:2,school:"evocation",effect:"A shimmering green arrow streaks toward a target within range and bursts in a spray of acid. The target takes acid damage immediately and additional acid damage at the end of its next turn.",damage:"4d4",damageType:"acid",rangeFeet:90,tags:["damage","ranged","dot"]},{id:"archer_scorching_ray",name:"Scorching Ray",level:2,school:"evocation",effect:"You focus elemental energy into a ray that deals fire damage to a target. The target ignites if it's wearing flammable gear.",damage:"4d6",damageType:"fire",rangeFeet:60,tags:["damage","ranged"]}],level3:[{id:"archer_fly",name:"Fly",level:3,school:"transmutation",effect:"You touch a willing creature. The target gains a flying speed of 60 feet for the duration. When the spell ends, the target falls if it is still aloft.",rangeFeet:30,concentration:!0,tags:["mobility","buff","ally"]},{id:"archer_lightning_bolt",name:"Lightning Bolt",level:3,school:"evocation",effect:"A stroke of lightning forming a line 100 feet long and 5 feet wide bursts from you in a direction you choose. Each creature in the line must make a Dexterity saving throw.",damage:"8d6",damageType:"lightning",save:"DEX",rangeFeet:90,tags:["damage","aoe"]},{id:"archer_gaseous_form",name:"Gaseous Form",level:3,school:"transmutation",effect:"You transform a willing creature you touch into a misty cloud for the duration. The creature can't talk or manipulate objects, and is immune to non-magical damage.",rangeFeet:30,concentration:!1,tags:["defense","utility","escape"]}]},brute:{archetype:"brute",cantrips:[{id:"brute_fire_bolt",name:"Fire Bolt",level:0,school:"evocation",effect:"You hurl a mote of fire at a creature or object within range. On a hit, the target takes fire damage.",damage:"1d10",damageType:"fire",rangeFeet:60,tags:["damage","ranged"]},{id:"brute_shillelagh",name:"Shillelagh",level:0,school:"transmutation",effect:"The wood of a club or quarterstaff you are holding becomes imbued with magic. For the duration, your weapon deals magical damage and has its damage die increased.",damage:"1d8",damageType:"force",rangeFeet:30,concentration:!0,tags:["buff","self","melee"]},{id:"brute_thorn_whip",name:"Thorn Whip",level:0,school:"conjuration",effect:"You create a long, vine-like whip covered in thorns that lashes out at your command toward a creature you can see.",damage:"1d6",damageType:"piercing",rangeFeet:30,tags:["damage","ranged","control"]}],level1:[{id:"brute_burning_hands",name:"Burning Hands",level:1,school:"evocation",effect:"As you hold your hands with thumbs touching and fingers spread, a thin sheet of flames shoots forth from your outstretched fingertips. Each creature in a 15-foot cone must make a Dexterity saving throw.",damage:"3d6",damageType:"fire",save:"DEX",rangeFeet:30,tags:["damage","aoe"]},{id:"brute_divine_favor",name:"Divine Favor",level:1,school:"evocation",effect:"Your prayer empowers you with divine energy. You gain advantage on the first attack roll you make during the spell's duration.",rangeFeet:30,tags:["buff","self"]},{id:"brute_magic_stone",name:"Magic Stone",level:1,school:"transmutation",effect:"You touch one to three pebbles and imbue them with magic. You or another creature can make a ranged spell attack with one of the stones, dealing bludgeoning damage.",damage:"1d6 + 1",damageType:"bludgeoning",rangeFeet:30,tags:["damage","ranged"]},{id:"brute_zephyr_strike",name:"Zephyr Strike",level:1,school:"evocation",effect:"You create a gust of wind that pushes a creature away from you. The target must succeed on a Strength saving throw or be pushed 15 feet away.",save:"STR",rangeFeet:30,tags:["control","push"]}],level2:[{id:"brute_shatter",name:"Shatter",level:2,school:"evocation",effect:"A sudden loud ringing sound, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution saving throw.",damage:"3d8",damageType:"thunder",save:"CON",rangeFeet:60,tags:["damage","aoe"]},{id:"brute_branding_smite",name:"Branding Smite",level:2,school:"evocation",effect:"The next time you hit a creature with a melee weapon attack during this spell's duration, your weapon flares with bright light and deals an extra 2d6 radiant damage.",damage:"2d6",damageType:"radiant",rangeFeet:30,tags:["damage","melee","buff"]},{id:"brute_spiritual_weapon",name:"Spiritual Weapon",level:2,school:"evocation",effect:"You create a spectral weapon within range. It can be used to attack, dealing force damage on a hit.",damage:"1d8 + 1",damageType:"force",rangeFeet:60,concentration:!1,tags:["summon","damage"]},{id:"brute_flame_blade",name:"Flame Blade",level:2,school:"evocation",effect:"You evoke a fiery blade in your free hand. The blade is similar in size and shape to a scimitar and sheds bright light in a 10-foot radius.",damage:"3d6",damageType:"fire",rangeFeet:30,concentration:!0,tags:["damage","melee"]}],level3:[{id:"brute_call_lightning",name:"Call Lightning",level:3,school:"conjuration",effect:"A storm cloud appears in the shape of a cylinder that is 10 feet tall with a 10-foot radius and centered on a point you can see within range.",damage:"5d6",damageType:"lightning",save:"DEX",rangeFeet:90,concentration:!0,tags:["damage","aoe","control"]},{id:"brute_elemental_weapon",name:"Elemental Weapon",level:3,school:"transmutation",effect:"A nonmagical weapon you touch becomes a magic weapon. Choose one of the following damage types: acid, cold, fire, lightning, or thunder.",rangeFeet:30,tags:["buff","melee","utility"]},{id:"brute_blur",name:"Blur",level:3,school:"illusion",effect:"Your body becomes blurred, shifting and wavering to all who can see you. Attacks against you have disadvantage unless the attacker can't see.",rangeFeet:30,concentration:!0,tags:["defense","debuff"]}]}},Ei={common:{cantrips:1,spells:1},uncommon:{cantrips:2,spells:2},elite:{cantrips:2,spells:3},boss:{cantrips:3,spells:4}},Mi={common:1,uncommon:2,elite:3,boss:4};class Ee{static generateSpellList(e){const{archetype:t,rarity:a,cr:i,seed:n}=e,r=new Se(n),o=bt[t],l=Ei[a],c=Mi[a],u=[],d=[...o.cantrips];for(let f=0;f<l.cantrips&&d.length>0;f++){const v=r.randomInt(0,d.length),w=d[v];u.push(w),d.splice(v,1)}const h=[],p=[1,2,3,4];for(const f of p){if(f>c)break;const v=o[`level${f}`];if(!v||v.length===0)continue;const w=vt[i]?.[f];if(!w||w===0)continue;const E=[...v];for(let I=0;I<w&&E.length>0;I++){const D=r.randomInt(0,E.length),x=E[D];h.push(x),E.splice(D,1)}}const g=l.spells-h.length;if(g>0){const f=[];for(const w of p){if(w>c)break;const E=o[`level${w}`];E&&f.push(...E)}const v=[...f];for(let w=0;w<g&&v.length>0;w++){const E=r.randomInt(0,v.length),I=v[E];h.some(D=>D.id===I.id)||h.push(I),v.splice(E,1)}}const m=Ee.getSpellSlotsForCR(i);return{cantrips:u,spells:h,slots:m}}static generateSpellListWithRNG(e){const{archetype:t,rarity:a,cr:i,rng:n}=e,r=bt[t],o=Ei[a],l=Mi[a],c=[],u=[...r.cantrips];for(let m=0;m<o.cantrips&&u.length>0;m++){const f=n.randomInt(0,u.length),v=u[f];c.push(v),u.splice(f,1)}const d=[],h=[1,2,3,4];for(const m of h){if(m>l)break;const f=r[`level${m}`];if(!f||f.length===0)continue;const v=vt[i]?.[m];if(!v||v===0)continue;const w=[...f];for(let E=0;E<v&&w.length>0;E++){const I=n.randomInt(0,w.length),D=w[I];d.push(D),w.splice(I,1)}}const p=o.spells-d.length;if(p>0){const m=[];for(const v of h){if(v>l)break;const w=r[`level${v}`];w&&m.push(...w)}const f=[...m];for(let v=0;v<p&&f.length>0;v++){const w=n.randomInt(0,f.length),E=f[w];d.some(I=>I.id===E.id)||d.push(E),f.splice(w,1)}}const g=Ee.getSpellSlotsForCR(i);return{cantrips:c,spells:d,slots:g}}static getSpellSlotsForCR(e){const t=Object.keys(vt).map(Number).sort((i,n)=>i-n);let a=0;for(const i of t)if(e>=i)a=i;else break;return vt[a]||{}}static archetypeCanCast(e){return Object.prototype.hasOwnProperty.call(bt,e)}static getSpellListForArchetype(e){return bt[e]}static spellToFeature(e){return{id:e.id,name:e.name,description:e.effect,type:e.level===0?"cantrip":"active",class:"Enemy",level:e.level,source:"spellcasting",tags:e.tags||[],isSpell:!0,school:e.school,damage:e.damage,damageType:e.damageType,save:e.save,range:e.rangeFeet,concentration:e.concentration}}static spellsToFeatures(e){const t=[];for(const a of e.cantrips)t.push(Ee.spellToFeature(a));for(const a of e.spells)t.push(Ee.spellToFeature(a));return t}static shouldHaveSpellcasting(e,t){return e==="support"||t==="elite"||t==="boss"}}const wt={brute:[{id:"tail_attack",name:"Tail Attack",description:"The boss makes a powerful tail strike against one creature within reach.",cost:1,effect:"Melee attack with bonus damage",damage:"2d8 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["melee","damage"]},{id:"devour",name:"Devour",description:"The boss devours a nearby creature, healing itself and dealing massive damage.",cost:3,effect:"Massive damage + self heal",damage:"4d12 + 5",damageType:"necrotic",archetypes:["brute"],tags:["melee","damage","heal"]},{id:"trample",name:"Trample",description:"The boss charges forward, crushing creatures in its path.",cost:2,effect:"AoE damage + prone",damage:"3d10 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["aoe","damage","control"]},{id:"earthquake_slam",name:"Earthquake Slam",description:"The boss strikes the ground, creating a shockwave that knocks nearby creatures prone.",cost:2,effect:"AoE damage + prone",damage:"2d10 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["aoe","damage","control"]},{id:"rage_smash",name:"Rage Smash",description:"The boss enters a rage and delivers a crushing blow with advantage.",cost:1,effect:"Single target attack with advantage",damage:"2d10 + 7",damageType:"bludgeoning",archetypes:["brute"],tags:["melee","damage","buff"]},{id:"charge",name:"Charge",description:"The boss moves up to its speed and makes a powerful attack.",cost:1,effect:"Movement + melee attack with bonus damage",damage:"2d8 + 5",damageType:"piercing",archetypes:["brute"],tags:["movement","melee","damage"]}],archer:[{id:"snipe",name:"Snipe",description:"The boss makes a precise ranged attack against one creature it can see.",cost:1,effect:"Ranged attack with advantage",damage:"2d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","damage"]},{id:"volley_shot",name:"Volley Shot",description:"The boss looses multiple arrows at different targets.",cost:2,effect:"Multiple ranged attacks (up to 3 targets)",damage:"1d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","aoe","damage"]},{id:"disabling_shot",name:"Disabling Shot",description:"The boss fires a shot aimed to disable a creature's movement.",cost:1,effect:"Ranged attack + restrained condition",damage:"1d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","control"]},{id:"evasive_maneuver",name:"Evasive Maneuver",description:"The boss dashes away without provoking opportunity attacks.",cost:1,effect:"Disengage + movement bonus",archetypes:["archer"],tags:["movement","defense"]},{id:"shadow_step",name:"Shadow Step",description:"The boss teleports to a nearby location and can make an attack.",cost:2,effect:"Teleport + optional ranged attack",damage:"2d6 + 4",damageType:"necrotic",archetypes:["archer"],tags:["movement","damage"]},{id:"multi_shot",name:"Multi-Shot",description:"The boss fires a barrage of arrows in a cone.",cost:3,effect:"Cone AoE damage",damage:"3d6 + 4",damageType:"piercing",archetypes:["archer"],tags:["aoe","damage"]}],support:[{id:"rally",name:"Rally",description:"The boss lets out a commanding cry, granting an ally an immediate attack.",cost:1,effect:"Adjacent ally makes immediate attack",archetypes:["support"],tags:["ally","buff"]},{id:"frightful_presence",name:"Frightful Presence",description:"The boss exudes an aura of terror, forcing creatures to make a fear save.",cost:1,effect:"AoE fear save or frightened",archetypes:["support"],tags:["aoe","control","fear"]},{id:"healing_aura",name:"Healing Aura",description:"The boss channels power, healing itself and nearby allies.",cost:2,effect:"Heal self + adjacent allies (3d6 + CHA mod)",archetypes:["support"],tags:["heal","ally","aoe"]},{id:"dispel_magic",name:"Dispel Magic",description:"The boss attempts to end a spell affecting it or an ally.",cost:1,effect:"Counter one spell effect",archetypes:["support"],tags:["utility","defense"]},{id:"command_ally",name:"Command Ally",description:"The boss telepathically commands an ally to take an action.",cost:2,effect:"Ally takes immediate action",archetypes:["support"],tags:["ally","control"]},{id:"shield_bash",name:"Shield Bash",description:"The boss strikes with its shield, dealing damage and potentially stunning.",cost:1,effect:"Melee damage + potential stun",damage:"2d6 + 3",damageType:"bludgeoning",archetypes:["support"],tags:["melee","damage","control"]},{id:"tactical_reposition",name:"Tactical Reposition",description:"The boss moves strategically to a better position without provoking opportunity attacks.",cost:1,effect:"Movement + disengage",archetypes:["support"],tags:["movement","defense"]}]},St=[{id:"teleport",name:"Teleport",description:"The boss instantly teleports to a nearby location without provoking opportunity attacks.",cost:2,effect:"Instant movement without opportunity attacks",archetypes:["brute","archer","support"],tags:["movement","defense"]},{id:"detect",name:"Detect",description:"The boss attunes its senses, detecting invisible or hidden creatures.",cost:1,effect:"Reveal invisible/hidden creatures nearby",archetypes:["brute","archer","support"],tags:["utility","information"]}],Di={1:3,2:3,3:3,4:3,5:3,6:3,7:3,8:3,9:3,10:3,11:4,12:4,13:4,14:4,15:4,16:5,17:5,18:5,19:5,20:5,21:6},Kr=3;class Te{static generate(e){const{archetype:t,cr:a,seed:i}=e,n=Te.getResistancesForCR(a),r=new Se(i),l=[...wt[t]||[],...St],c=Te.selectLegendaryActions(l,r),u=r.random()<.2?Te.generateLairActionHint(t,r):void 0;return{resistances:n,actions:c,lairActionHint:u}}static generateWithRNG(e){const{archetype:t,cr:a,rng:i}=e,n=Te.getResistancesForCR(a),o=[...wt[t]||[],...St],l=Te.selectLegendaryActions(o,i),c=i.random()<.2?Te.generateLairActionHint(t,i):void 0;return{resistances:n,actions:l,lairActionHint:c}}static selectLegendaryActions(e,t){const a=e.filter(c=>c.tags?.includes("movement"));e.filter(c=>!c.tags?.includes("movement"));const i=[],n=new Set;if(a.length>0){const c=a.filter(u=>!n.has(u.id));if(c.length>0){const u=t.randomChoice(c);i.push(u),n.add(u.id)}}const r=Kr-i.length,o=e.filter(c=>!n.has(c.id)),l=t.shuffle(o);for(let c=0;c<r&&c<l.length;c++)i.push(l[c]);return i}static getResistancesForCR(e){const t=Object.keys(Di).map(Number).sort((a,i)=>i-a);for(const a of t)if(e>=a)return Di[a];return 3}static generateLairActionHint(e,t){const a=["Cave-in risk: Sections of ceiling may collapse on initiative count 20","Lair tremors: DEX save or fall prone at start of each round","Crushing walls: Walls slowly close in, dealing damage each round"],o={brute:a,archer:["Covering fire: Minions provide ranged support from elevated positions","Trapped corridors: DEX save or trigger arrow traps/magical wards","Shadow passages: Boss can teleport between pre-designated shadow points"],support:["Aura of command: Allies gain advantage on attacks within 30ft of boss","Reinforcement calls: Minions arrive in waves on rounds 3 and 5","Lair wards: Magical barriers provide boss temporary protection"]}[e]||a;return t.randomChoice(o)}static getActionById(e){for(const t of Object.values(wt)){const a=t.find(i=>i.id===e);if(a)return a}return St.find(t=>t.id===e)}static getActionsForArchetype(e){return[...wt[e]||[],...St]}static shouldHaveLegendary(e){return e==="boss"}}const Jr={baseMultiplier:1,levelOffset:0,customCurve:new Map,minLevel:0,maxLevel:1/0};function Qr(s,e=Jr){if(e.customCurve.has(s))return e.customCurve.get(s);let t=s*e.baseMultiplier+e.levelOffset;return t=Math.max(e.minLevel,Math.min(e.maxLevel,t)),t}const eo={common:.9,uncommon:.95,elite:1,boss:1.05},to=.2,ao=[{minLevel:1,die:"1d6"},{minLevel:3,die:"1d8"},{minLevel:6,die:"1d10"},{minLevel:10,die:"1d12"},{minLevel:15,die:"2d6"},{minLevel:20,die:"2d8"},{minLevel:25,die:"2d10"},{minLevel:30,die:"3d8"}],Ls=.2,io=.2;function Os(s){switch(s){case"brute":return"STR";case"archer":return"DEX";case"support":return"CHA"}}function Ri(s,e,t){const a=Oe(t),i=eo[t],n=Math.pow(e,i),r=Bs(e);return Math.round(s*n*a.hpMultiplier*r)}function Bs(s){return s>=1?1:s<=0?.5:.5+.5*s}function _t(s,e,t,a){const i=Os(a),n=Math.floor(((s[i]??10)-10)/2),r=Math.ceil(1+(e-1)/4),o=so(e),l=Math.floor(Ls*(e-1)),c=n+l,u=Math.floor(to*(e-1)),d=r+n+u;return{damageDie:o,damageModifier:c,attackBonus:d}}function so(s){let e="d6";for(const t of ao)s>=t.minLevel&&(e=t.die);return e}function Pi(s,e,t,a){const i=Math.floor((s.DEX-10)/2);let n=t;a.armor?.acBonus&&(n=a.armor.acBonus);let r=0;a.shield?.acBonus&&(r=a.shield.acBonus);const o=Math.floor(io*(e-1));return n+i+r+o}function no(s,e,t){const a=Os(t),i=Math.floor(((s[a]??10)-10)/2),n=Math.floor(Ls*(e-1));return i+n}const C=class C{static getSeededRNG(e,t){const a=t!==void 0?`${e}-${t}`:e;return new Se(a)}static applyAudioStatInfluence(e,t){const{bass_dominance:a=0,mid_dominance:i=0,treble_dominance:n=0}=t;let r=0,o=0,l=0,c=0,u=0;return a>.6?(r=1,l=1):a>.4&&(r=1),n>.6&&(o=1),i>.6?(c=1,u=1):i>.4&&(c=1),a<.5&&i<.5&&n<.5&&Math.abs(a-i)<.2&&Math.abs(i-n)<.2&&(r=1,o=1,l=1,c=1,u=1),{STR:Math.min(e.STR+r,e.STR+C.MAX_AUDIO_INFLUENCE),DEX:Math.min(e.DEX+o,e.DEX+C.MAX_AUDIO_INFLUENCE),CON:Math.min(e.CON+l,e.CON+C.MAX_AUDIO_INFLUENCE),INT:e.INT,WIS:Math.min(e.WIS+c,e.WIS+C.MAX_AUDIO_INFLUENCE),CHA:Math.min(e.CHA+u,e.CHA+C.MAX_AUDIO_INFLUENCE)}}static scaleStatsForRarity(e,t,a){let n=Oe(t).statMultiplier;return a!==void 0&&(n=C.getStatMultiplierForFractionalCR(a)*n),{STR:Math.round(e.STR*n),DEX:Math.round(e.DEX*n),CON:Math.round(e.CON*n),INT:Math.round(e.INT*n),WIS:Math.round(e.WIS*n),CHA:Math.round(e.CHA*n)}}static getTemplateById(e){return Xe.find(t=>t.id===e)}static selectTemplate(e,t,a,i){let n=[...Xe];if(t&&(n=n.filter(r=>r.category===t)),a&&(n=n.filter(r=>r.archetype===a)),n.length===0)throw new Error(`No enemy templates match filters: category=${t}, archetype=${a}`);return i?C.weightedSelectionByAudio(n,i,e):e.randomChoice(n)}static weightedSelectionByAudio(e,t,a){const i=e.map(n=>{const{audioPreference:r}=n,o=(t.bass_dominance||0)*r.bass+(t.mid_dominance||0)*r.mid+(t.treble_dominance||0)*r.treble;return[n,Math.max(.1,o)]});return a.weightedChoice(i)}static scaleSignatureAbility(e,t,a,i,n,r){const o=Oe(t),l=r?.attackLevel!==void 0?_t(a,r.attackLevel,t,n).damageDie:`1d${o.signatureDieSize}`,c=r?.attackLevel!==void 0?_t(a,r.attackLevel,t,n).damageModifier:no(a,i,n);return{id:e.id,name:e.name,description:e.description,type:"active",class:"Enemy",level:1,source:"default",tags:[e.attackType,e.damageType],effects:[],attack:{name:e.name,damage:`${l} + ${c}`,damage_dice:l,damage_type:e.damageType,type:e.attackType,range:e.range,properties:e.properties}}}static generateBossName(e,t){const a=t.random();if(a<.5){const r=t.randomChoice(C.BOSS_TITLES);return`${e}, ${r}`}if(a<.8)return`${t.randomChoice(C.BOSS_PREFIXES)} ${e}`;const i=["the Void","Ruin","Pain","Death","the Abyss","the Forgotten Lands","Eternal Night","Chaos","Doom","the Broken World","Shadows","the Endless Void"],n=t.randomChoice(i);return`${e} of ${n}`}static generateBossFeatures(e,t,a){const i=[],n={...t};if(n.attack&&typeof n.attack=="object"){const o=n.attack;if(o.damage_dice){const l=o.damage_dice.match(/^d(\d+)$/);if(l){const c=parseInt(l[1],10);o.damage_dice=`2d${c}`;const u=o.damage?.match(/^(\d+d\d+)(\s*\+\s*\d+)?$/);if(u){const d=u[1],h=u[2]||"";o.damage=`${d.replace(/^d\d+$/,`2d${c}`)}${h}`}}}}i.push(n);const r=C.generateUltimateAbility(e.archetype,a);return i.push(r),i}static generateUltimateAbility(e,t){const a=[{id:"cataclysmic_smash",name:"Cataclysmic Smash",description:"The boss unleashes all its power in a devastating blow that cracks the earth itself.",damage:"6d12 + 10",damageType:"bludgeoning"},{id:"unbreaking_frenzy",name:"Unbreaking Frenzy",description:"The boss enters a state of pure rage, attacking everything nearby with impossible speed.",damage:"4d10 + 8",damageType:"slashing"},{id:"world_ender",name:"World Ender",description:"A single attack charged with all the boss's strength, capable of felling the mightiest foes.",damage:"8d8 + 12",damageType:"bludgeoning"}],i=[{id:"phantom_ barrage",name:"Phantom Barrage",description:"The boss fires ethereal arrows that phase through cover, striking all enemies in a massive cone.",damage:"5d8 + 8",damageType:"piercing"},{id:"death_from_above",name:"Death from Above",description:"The boss leaps impossibly high, then rains destruction in a devastating area attack.",damage:"4d10 + 10",damageType:"piercing"},{id:"final_Stand",name:"Final Stand",description:"With uncanny precision, the boss fires a single arrow that cannot miss, dealing massive damage.",damage:"10d6 + 15",damageType:"piercing"}],n=[{id:"dark_miracle",name:"Dark Miracle",description:"The boss performs a ritual of incredible power, completely healing itself and all allies.",effect:"Heal self and all allies for 50% max HP"},{id:"soul_anchor",name:"Soul Anchor",description:"The boss binds the souls of all creatures nearby, preventing all movement and actions for a duration.",effect:"AoE stun/paralyze all creatures, no save"},{id:"realm_of_despair",name:"Realm of Despair",description:"The boss projects an aura of pure hopelessness, weakening all enemies' attacks and defenses.",effect:"All enemies have disadvantage on attacks and -2 AC"}];let r,o="bludgeoning";switch(e){case"brute":r=t.randomChoice(a),o="bludgeoning";break;case"archer":r=t.randomChoice(i),o="piercing";break;case"support":r=t.randomChoice(n);break;default:r=t.randomChoice(a),o="bludgeoning"}const l={id:r.id,name:r.name,description:r.description,type:"ultimate",class:"Enemy",level:1,source:"boss",tags:["ultimate","once_per_encounter",e],effects:[],uses_per_encounter:1,max_uses_per_encounter:1};if("damage"in r){const c=r;l.attack={name:c.name,damage:c.damage,damage_dice:c.damage,damage_type:o,type:e==="archer"?"ranged":"melee",properties:["ultimate","once_per_encounter"]}}else{const c=r;l.effect=c.effect}return l}static generateAbilities(e,t,a,i,n,r,o){const c=[n&&r?C.scaleSignatureAbility(e.signatureAbility,t,n,r,e.archetype,o):C.scaleSignatureAbility(e.signatureAbility,t,e.baseStats,1,e.archetype,o)];let u;if(Ee.shouldHaveSpellcasting(e.archetype,t)){const p=i||C.getCRForRarity(t);u=Ee.generateSpellListWithRNG({archetype:e.archetype,rarity:t,cr:p,rng:a});const g=Ee.spellsToFeatures(u);c.push(...g)}const h=Oe(t).extraAbilityCount;if(h>0){const p=C.selectExtraAbilities(e.archetype,h,a);c.push(...p)}return{abilities:c,spellConfig:u}}static selectExtraAbilities(e,t,a){const i=C.ARCHETYPE_TAGS[e],r=re.getInstance().getAllClassFeatures(),o=[];for(const d of r.values())o.push(...d);const l=o.filter(d=>!d.tags||d.tags.length===0?!1:d.tags.some(h=>i.some(p=>h.toLowerCase().includes(p.toLowerCase())||p.toLowerCase().includes(h.toLowerCase()))));if(l.length===0)return[];const c=[],u=[...l];for(let d=0;d<t&&u.length>0;d++){const h=a.randomInt(0,u.length);c.push(u[h]),u.splice(h,1)}return c.map(d=>({id:d.id,name:d.name,description:d.description,type:d.type,class:"Enemy",level:1,source:d.source,tags:d.tags||[],effects:d.effects||[]}))}static generate(e){const{seed:t,cr:a,level:i,templateId:n,rarity:r="common",difficultyMultiplier:o=1,audioProfile:l,track:c,statLevels:u}=e;if(l&&!c)throw new Error("track is required when audioProfile is provided");const d=C.getSeededRNG(t);let h;if(n){const P=C.getTemplateById(n);if(!P)throw new Error(`Unknown enemy template ID: ${n}`);h=P}else{const P=e.category,L=e.archetype;h=C.selectTemplate(d,P,L,l)}const p=a??C.getCRForRarity(r),g=i??C.getLevelFromCR(p);let m=C.scaleStatsForRarity(h.baseStats,r,a);l&&(m=C.applyAudioStatInfluence(m,l));const f={STR:Math.floor((m.STR-10)/2),DEX:Math.floor((m.DEX-10)/2),CON:Math.floor((m.CON-10)/2),INT:Math.floor((m.INT-10)/2),WIS:Math.floor((m.WIS-10)/2),CHA:Math.floor((m.CHA-10)/2)};Oe(r);let v;u?.hpLevel!==void 0?v=Ri(h.baseHP,u.hpLevel,r):v=Ri(h.baseHP,g,r),o!==1&&(v=Math.round(v*o));const w=Math.ceil(1+(g-1)/4);Ee.shouldHaveSpellcasting(h.archetype,r);const{abilities:E,spellConfig:I}=C.generateAbilities(h,r,d,p,m,g,u);let D=E;if(r==="boss"){const P=D[0];D=C.generateBossFeatures(h,P,d)}let x=[],N;if(Te.shouldHaveLegendary(r)){const P=C.getSeededRNG(`${t}-legendary`);N=Te.generateWithRNG({archetype:h.archetype,cr:p,rng:P}),x=N.actions.map(L=>({id:L.id,name:L.name,description:L.description,type:"active",class:"Enemy",level:1,source:"legendary",tags:L.tags||["legendary"],effects:[],legendary_cost:L.cost,legendary_effect:L.effect,legendary_damage:L.damage,legendary_damage_type:L.damageType})),D.push(...x)}const $=Ge.generate({archetype:h.archetype,rarity:r,seed:`${t}-equipment`});let F,te;if(u?.attackLevel!==void 0){const P=_t(m,u.attackLevel,r,h.archetype);F=P.damageDie,te=P.damageModifier}else{const P=_t(m,g,r,h.archetype);F=P.damageDie,te=P.damageModifier}const Q=[];if($.weapon){const P=Ge.getEquipmentName($.weapon.id),L=Ze[P];L&&Q.push({name:P,damage:`${F} + ${te}`,damage_dice:F,damage_type:L.damage?.damageType||h.signatureAbility.damageType,type:h.signatureAbility.attackType,range:h.signatureAbility.range,properties:$.weapon.properties||[],equipped:!0})}Q.length===0&&Q.push({name:h.signatureAbility.name,damage:`${F} + ${te}`,damage_dice:F,damage_type:h.signatureAbility.damageType,type:h.signatureAbility.attackType,range:h.signatureAbility.range,properties:h.signatureAbility.properties||[],equipped:!0});const X=[];let de=0;if($.armor){const P=Ge.getEquipmentName($.armor.id);X.push(P),$.armor.acBonus&&(de=$.armor.acBonus-h.baseAC)}if($.shield){const P=Ge.getEquipmentName($.shield.id);X.push(P),$.shield.acBonus&&(de+=$.shield.acBonus)}let ee;if(u?.defenseLevel!==void 0?ee=Pi(m,u.defenseLevel,h.baseAC,$):ee=Pi(m,g,h.baseAC,$),a!==void 0){const P=h.archetype==="brute"?"STR":h.archetype==="archer"?"DEX":"CHA",L=C.estimateWeaponDPR(F,te,w,f[P]),_e=C.getMinHPRatio(a),ge=Math.ceil(L*_e);v<ge&&(v=ge)}return{name:r==="boss"?C.generateBossName(h.name,d):h.name,race:"Enemy",class:"Monster",subrace:r,level:g,ability_scores:m,ability_modifiers:f,proficiency_bonus:w,hp:{current:v,max:v,temp:0},armor_class:ee,initiative:f.DEX,speed:h.baseSpeed,skills:{},saving_throws:{STR:!0,DEX:!0,CON:!0,INT:!1,WIS:!1,CHA:!1},racial_traits:[],class_features:D.map(P=>P.id||P.name||`${P}`),equipment:{weapons:Q,armor:X,items:[],totalWeight:0,equippedWeight:0},spells:I?{spell_slots:Object.fromEntries(Object.entries(I.slots).map(([P,L])=>[Number(P),{total:L,used:0}])),known_spells:I.spells.map(P=>P.name),cantrips:I.cantrips.map(P=>P.name)}:{spell_slots:{},known_spells:[],cantrips:[]},...I?{combat_spells:[...I.cantrips,...I.spells]}:{},xp:{current:0,next_level:0},seed:t,generated_at:new Date().toISOString(),...p!==void 0?{cr:p}:{},...u?{stat_levels:u}:{},...N?{legendary_config:{resistances_per_day:N.resistances,actions:N.actions.map(P=>({id:P.id,name:P.name,cost:P.cost,effect:P.effect,damage:P.damage,damage_type:P.damageType,description:P.description,tags:P.tags})),lair_action_hint:N.lairActionHint}}:{}}}static getLevelFromCR(e){return Qr(e)}static getStatMultiplierForFractionalCR(e){return Bs(e)}static getDamageDieForRarity(e){return`1d${Oe(e).signatureDieSize}`}static getCRForRarity(e){return{common:.25,uncommon:.5,elite:1,boss:2}[e]||.25}static estimateWeaponDPR(e,t,a,i){const n=e.match(/(\d+)d(\d+)/);if(!n)return 0;const r=parseInt(n[1]),o=parseInt(n[2]),l=r*(o+1)/2+t,c=a+i,u=Math.max(.05,(10+c)/20);return l*u}static getMinHPRatio(e){return 3+(e-1)*(3/29)}static generateEncounter(e,t){const{seed:a,count:i,difficulty:n="medium",baseRarity:r="common",scaleRarityWithCR:o=!1,difficultyMultiplier:l=1,category:c,archetype:u,templateId:d,enemyMix:h="uniform",templates:p,audioProfile:g,track:m,enableLeaderPromotion:f=!0}=t;if(g&&!m)throw new Error("track is required when audioProfile is provided");if(i<=0)return[];if(r==="boss"&&i>1)throw new Error(`Boss encounters must have count=1. Requested count=${i} with baseRarity='boss'. Boss enemies are designed for solo encounters against a party. Use a lower rarity (common/uncommon/elite) for multi-enemy encounters, or set count=1 for a boss encounter.`);const v=e.map(X=>X.level),w=Wt(v,n),E=Math.round(w*l),I=ot(i),D=Math.round(E/I/i);let x=Ta(D);const N=C.getSeededRNG(a),$=N.random();$<.25?x=Math.min(x*1.5,30):$>.75&&(x=Math.max(x*.67,.125));const F=o?C.getRarityDistribution(i,x):Array(i).fill(r),te=C.selectTemplatesForMix(i,N,h,p,c,u,g),Q=[];for(let X=0;X<i;X++){const de=te[X];if(!de)throw new Error(`No template available for enemy at index ${X}`);const ee=C.generate({seed:`${a}-${X}`,templateId:de.id,cr:x,rarity:F[X],difficultyMultiplier:l,audioProfile:g,track:m});Q.push(ee)}return f&&!o&&C.applyLeaderPromotion(Q,N,r),Q}static generateEncounterByCR(e){const{seed:t,count:a=1,targetCR:i=1,baseRarity:n="common",scaleRarityWithCR:r=!1,difficultyMultiplier:o=1,category:l,archetype:c,templateId:u,enemyMix:d="uniform",templates:h,audioProfile:p,track:g,enableLeaderPromotion:m=!0,statLevels:f}=e;if(p&&!g)throw new Error("track is required when audioProfile is provided");if(a<=0)return[];if(i<0)throw new Error(`Invalid targetCR: ${i}. Must be >= 0`);if(n==="boss"&&a>1)throw new Error(`Boss encounters must have count=1. Requested count=${a} with baseRarity='boss'. Boss enemies are designed for solo encounters against a party. Use a lower rarity (common/uncommon/elite) for multi-enemy encounters, or set count=1 for a boss encounter.`);const v=ot(a);let w=i;v>1&&a>1&&(w=Math.max(i/Math.sqrt(v),.125));const E=r?C.getRarityDistribution(a,i):Array(a).fill(n),I=C.getSeededRNG(t),D=C.selectTemplatesForMix(a,I,d,h,l,c,p),x=[];for(let N=0;N<a;N++){const $=D[N];if(!$)throw new Error(`No template available for enemy at index ${N}`);const F=C.generate({seed:`${t}-${N}`,templateId:$.id,cr:w,rarity:E[N],difficultyMultiplier:o,audioProfile:p,track:g,...f?{statLevels:f}:{}});x.push(F)}return m&&!r&&C.applyLeaderPromotion(x,I,n),x}static selectTemplatesForMix(e,t,a,i,n,r,o){const l=[];if(a==="custom"&&i)for(let c=0;c<e;c++){const u=i[c%i.length],d=C.getTemplateById(u);if(!d)throw new Error(`Unknown template ID in custom mix: ${u}`);l.push(d)}else if(a==="category"){if(!n)throw new Error('category option is required when using enemyMix: "category"');for(let c=0;c<e;c++){const u=C.selectTemplate(t,n,r,o);l.push(u)}}else if(a==="random")for(let c=0;c<e;c++){const u=C.selectTemplate(t,void 0,r,o);l.push(u)}else{const c=C.selectTemplate(t,n,r,o);for(let u=0;u<e;u++)l.push(c)}return l}static applyLeaderPromotion(e,t,a){const i=e.length;if(i<4)return;const n=[];if(i>=4&&i<=6){const r=t.randomInt(0,i),o=C.promoteRarity(a,1);n.push({index:r,rarity:o})}else if(i>=7&&i<=9){const r=t.randomInt(0,i),o=C.promoteRarity(a,2);n.push({index:r,rarity:o})}else if(i>=10){const r=t.randomInt(0,i);let o=t.randomInt(0,i);for(;o===r&&i>1;)o=t.randomInt(0,i);n.push({index:r,rarity:C.promoteRarity(a,1)},{index:o,rarity:C.promoteRarity(a,2)})}for(const r of n){const o=e[r.index],l=o.name.toLowerCase().replace(/\s+/g,"-"),c=C.getTemplateById(l);if(c){const u=C.generate({seed:o.seed+"-promoted",templateId:c.id,rarity:r.rarity});e[r.index]=u}}}static calculateUpgradePoints(e){return e>=C.CR_TIERS.EPIC.min?6:e>=C.CR_TIERS.VERY_HIGH.min?5:e>=C.CR_TIERS.HIGH.min?4:e>=C.CR_TIERS.MEDIUM_HIGH.min?3:e>=C.CR_TIERS.MEDIUM.min?2:e>=C.CR_TIERS.LOW_MEDIUM.min?1:0}static upgradeRarity(e){return e==="common"?"uncommon":"elite"}static getRarityDistribution(e,t){const a=C.calculateUpgradePoints(t),i=Array(e).fill("common");for(let n=0;n<a;n++){const r=n%e;i[r]=C.upgradeRarity(i[r])}return i}static promoteRarity(e,t){const a=["common","uncommon","elite","boss"],i=a.indexOf(e),n=Math.min(i+t,a.length-1);return a[n]}};C.ARCHETYPE_TAGS={brute:["combat","damage","defense","melee","durability"],archer:["combat","ranged","accuracy","mobility","stealth"],support:["support","healing","buff","control","utility"]},C.MAX_AUDIO_INFLUENCE=2,C.BOSS_TITLES=["the Destroyer","the Unbreakable","the Annihilator","the Fearless","the Ancient","Lord of Ruin","the World-Ender","the Eternal","King of Ruin","the Indomitable","the Void Walker","Master of Destruction","the Fallen","the Ascendant","the Heartless","Sovereign of Pain","the Soul Render","the Undying","the Chaos Bringer","Lord of Death","the World Breaker","the Timeless","the Forgotten"],C.BOSS_PREFIXES=["Great","Eternal","Ancient","Dread","High","Dark","Void","Blood","Iron","Storm"],C.CR_TIERS={LOW:{min:0,max:2,upgrades:0},LOW_MEDIUM:{min:3,max:5,upgrades:1},MEDIUM:{min:6,max:10,upgrades:2},MEDIUM_HIGH:{min:11,max:15,upgrades:3},HIGH:{min:16,max:20,upgrades:4},VERY_HIGH:{min:21,max:30,upgrades:5},EPIC:{min:31,max:1/0,upgrades:6}};let Ke=C;class pt{run(e,t,a){const i=new Fs(a,e,t);let n=!1;for(let r=0;r<a.runCount;r++){if(a.abortSignal?.aborted){n=!0;break}const o=`${a.baseSeed}-${r}`,l=As(o),c=a.enemyRegeneration?Ke.generateEncounterByCR({...a.enemyRegeneration,seed:`${a.enemyRegeneration.seed}-${r}`}):t,d=new _s().runFullCombat(e,c,a.aiConfig,a.combatConfig,l);i.aggregateRun(d,r,o),a.onProgress&&a.onProgress(r+1,a.runCount)}return i.getResults(n)}}class Fs{constructor(e,t,a){this.playerWins=0,this.enemyWins=0,this.draws=0,this.roundsList=[],this.roundsOnWin=[],this.roundsOnLoss=[],this.playerHPPercentRemainingOnWin=[],this.playerDeathRounds=[],this.enemyDeathRounds=[],this.combatantAccumulators=new Map,this.runDetails=null,this.enemyGenTracker=new Map,this.config=e;const i=t.map(o=>o.level??1);this.party={memberCount:t.length,averageLevel:i.length>0?i.reduce((o,l)=>o+l,0)/i.length:0,memberNames:t.map(o=>o.name)};const n=a.map(o=>o.cr??o.level??1);this.encounter={enemyCount:a.length,averageCR:n.length>0?n.reduce((o,l)=>o+l,0)/n.length:0,enemyNames:a.map(o=>o.name)};const r=!!e.enemyRegeneration;this.combatantIds=[...t.map((o,l)=>({id:`player_${l}`,name:o.name,side:"player"})),...a.map((o,l)=>({id:`enemy_${t.length+l}`,name:r?`Enemy ${l+1}`:o.name,side:"enemy"}))];for(const{id:o,name:l,side:c}of this.combatantIds)this.combatantAccumulators.set(o,new Hs(o,l,c));e.collectDetailedLogs&&(this.runDetails=[])}aggregateRun(e,t,a){const{combat:i,result:n,metrics:r}=e;switch(n.winnerSide){case"player":this.playerWins++;break;case"enemy":this.enemyWins++;break;case"draw":this.draws++;break}const o=n.roundsElapsed;if(this.roundsList.push(o),n.winnerSide==="player"&&this.roundsOnWin.push(o),n.winnerSide==="enemy"&&this.roundsOnLoss.push(o),n.winnerSide==="player"&&e.combat){const l=e.combat.combatants.filter(c=>c.id.startsWith("player"));if(l.length>0){const c=l.reduce((d,h)=>d+h.character.hp.max,0),u=l.reduce((d,h)=>d+h.currentHP,0);c>0&&this.playerHPPercentRemainingOnWin.push(u/c)}}if(e.combat){for(const l of e.combat.combatants)if(l.isDefeated){const u=r.get(l.id)?.roundsSurvived??o;l.id.startsWith("player")?this.playerDeathRounds.push(u):this.enemyDeathRounds.push(u)}}for(const[l,c]of r){const u=this.combatantAccumulators.get(l);u&&u.accumulate(c,o)}if(this.config.enemyRegeneration&&i)for(const l of i.combatants){if(!l.id.startsWith("enemy"))continue;const c=l.character.name,u=this.enemyGenTracker.get(c)??{count:0,hps:[],acs:[],crs:[]};u.count++,u.hps.push(l.character.hp.max),u.acs.push(l.character.armor_class),u.crs.push(l.character.cr??l.character.level??1),this.enemyGenTracker.set(c,u)}this.runDetails!==null&&this.runDetails.push({runIndex:t,seed:a,combat:i,result:n,metrics:new Map(r)})}getResults(e=!1){const t=this.roundsList.length,a={totalRuns:t,playerWins:this.playerWins,enemyWins:this.enemyWins,draws:this.draws,playerWinRate:t>0?this.playerWins/t:0,averageRounds:this.average(this.roundsList),medianRounds:this.median(this.roundsList),averageRoundsOnWin:this.average(this.roundsOnWin),averageRoundsOnLoss:this.average(this.roundsOnLoss),averagePlayerHPPercentRemaining:this.average(this.playerHPPercentRemainingOnWin)*100,totalPlayerDeaths:this.playerDeathRounds.length,averageRoundsPerPlayerDeath:this.average(this.playerDeathRounds),totalEnemyDeaths:this.enemyDeathRounds.length,averageRoundsPerEnemyDeath:this.average(this.enemyDeathRounds)},i=new Map;for(const[r,o]of this.combatantAccumulators)i.set(r,o.toSimulationMetrics(t));let n;return this.enemyGenTracker.size>0&&(n=Array.from(this.enemyGenTracker.entries()).sort((r,o)=>o[1].count-r[1].count).map(([r,o])=>({name:r,count:o.count,hpRange:{min:Math.min(...o.hps),max:Math.max(...o.hps),avg:this.average(o.hps)},acRange:{min:Math.min(...o.acs),max:Math.max(...o.acs),avg:this.average(o.acs)},crRange:{min:Math.min(...o.crs),max:Math.max(...o.crs),avg:this.average(o.crs)}}))),{config:this.config,summary:a,party:this.party,encounter:this.encounter,perCombatantMetrics:i,enemyGenerationStats:n,runDetails:this.runDetails!==null&&this.runDetails.length>0?this.runDetails:void 0,wasCancelled:e}}average(e){return e.length===0?0:e.reduce((t,a)=>t+a,0)/e.length}median(e){if(e.length===0)return 0;const t=[...e].sort((i,n)=>i-n),a=Math.floor(t.length/2);return t.length%2===0?(t[a-1]+t[a])/2:t[a]}}class Hs{constructor(e,t,a){this.totalDamageDealtSum=0,this.totalDamageTakenSum=0,this.totalHealingDoneSum=0,this.roundsSurvivedSum=0,this.survivalCount=0,this.killCount=0,this.criticalHitSum=0,this.hitsSum=0,this.missesSum=0,this.totalAttackActions=0,this.spellsCastSum=0,this.actionTypeCounts=new Map,this.damagePerRoundList=[],this.hpRemainingList=[],this.id=e,this.name=t,this.side=a}accumulate(e,t){this.totalDamageDealtSum+=e.totalDamageDealt,this.totalDamageTakenSum+=e.totalDamageTaken,this.totalHealingDoneSum+=e.totalHealingDone,this.roundsSurvivedSum+=e.roundsSurvived,e.survived&&this.survivalCount++;const a=e.actionsByType.attack??0;this.criticalHitSum+=e.criticalHits,this.hitsSum+=e.hits,this.missesSum+=e.misses,this.killCount+=e.kills,this.totalAttackActions+=a,this.spellsCastSum+=e.spellsCast;for(const[i,n]of Object.entries(e.actionsByType))this.actionTypeCounts.set(i,(this.actionTypeCounts.get(i)??0)+n);if(e.roundsSurvived>0&&e.damagePerRound.length>0){const i=e.damagePerRound.reduce((n,r)=>n+r,0)/e.damagePerRound.length;this.damagePerRoundList.push(i)}e.survived&&e.hpRemainingPercent>0&&this.hpRemainingList.push(e.hpRemainingPercent)}toSimulationMetrics(e){const t=e>0?e:1;let a="attack",i=0;for(const[o,l]of this.actionTypeCounts)l>i&&(i=l,a=o);const n=xi(this.damagePerRoundList,20),r=xi(this.hpRemainingList,20);return{combatantId:this.id,name:this.name,side:this.side,averageDamagePerRound:this.average(this.damagePerRoundList),medianDamagePerRound:this.median(this.damagePerRoundList),averageTotalDamageDealt:this.totalDamageDealtSum/t,averageTotalDamageTaken:this.totalDamageTakenSum/t,averageHealingDone:this.totalHealingDoneSum/t,averageRoundsSurvived:this.roundsSurvivedSum/t,survivalRate:this.survivalCount/t,killRate:this.killCount/t,criticalHitRate:this.totalAttackActions>0?this.criticalHitSum/this.totalAttackActions:0,averageHitRate:this.hitsSum+this.missesSum>0?this.hitsSum/(this.hitsSum+this.missesSum):0,averageHitsPerRun:this.hitsSum/t,averageMissesPerRun:this.missesSum/t,averageSpellSlotsUsed:this.spellsCastSum/t,mostUsedAction:a,damageDistribution:n,hpRemainingDistribution:r}}average(e){return e.length===0?0:e.reduce((t,a)=>t+a,0)/e.length}median(e){if(e.length===0)return 0;const t=[...e].sort((i,n)=>i-n),a=Math.floor(t.length/2);return t.length%2===0?(t[a-1]+t[a])/2:t[a]}}function xi(s,e=20){if(s.length===0)return[];const t=Math.min(...s),a=Math.max(...s);if(t===a)return[{rangeStart:t,rangeEnd:a,count:s.length,percent:100}];const n=(a-t)/e,r=[];for(let o=0;o<e;o++)r.push({rangeStart:t+o*n,rangeEnd:t+(o+1)*n,count:0});for(const o of s){const l=Math.min(o,a);let c=Math.floor((l-t)/n);c>=e&&(c=e-1),r[c].count++}return r.map(o=>({rangeStart:o.rangeStart,rangeEnd:o.rangeEnd,count:o.count,percent:s.length>0?o.count/s.length*100:0}))}const Pa={easy:{min:.9,max:1},medium:{min:.7,max:.8},hard:{min:.5,max:.6},deadly:{min:.3,max:.4}};class ro{validate(e,t,a,i){const r=new pt().run(e,t,i);return this.analyze(r,a)}analyze(e,t){const{summary:a}=e,i=a.playerWinRate,n=Pa[t],r=this.classifyDifficulty(i),o=this.calculateBalanceScore(i,n),l=this.classifyVariance(i,n),c=this.calculateConfidence(a.totalRuns),u=this.generateRecommendations(i,n,r,t,c,a);return{intendedDifficulty:t,actualDifficulty:r,balanceScore:o,playerWinRate:i,expectedWinRate:n,difficultyVariance:l,confidence:c,recommendations:u,averagePlayerHPPercentRemaining:a.averagePlayerHPPercentRemaining,totalRuns:a.totalRuns}}classifyDifficulty(e){return e>=.9?"easy":e>=.7?"medium":e>=.5?"hard":"deadly"}calculateBalanceScore(e,t){const a=(t.min+t.max)/2,i=(t.max-t.min)/2,n=Math.abs(e-a),r=i>0?n/i:0;if(n<=i){const u=70+30*(1-r);return Math.round(Math.min(100,Math.max(0,u)))}const o=n-i,c=70-Math.min(70,o/.5*70);return Math.round(Math.min(100,Math.max(0,c)))}classifyVariance(e,t){return e>t.max?"underpowered":e<t.min?"overpowered":"balanced"}calculateConfidence(e){return e<=0?0:Math.min(1,1-1/Math.sqrt(e))}generateRecommendations(e,t,a,i,n,r){const o=[],l=e>t.max;if(e<t.min){const u=t.min-e,d=r.totalEnemyDeaths>0?Math.round(r.totalEnemyDeaths/r.totalRuns):1;u>.3?(o.push({description:"Reduce enemy CR by 1-2 levels",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),o.push({description:`Reduce enemy count by ${Math.max(1,Math.ceil(d*.3))}`,expectedImpact:`+${Math.round(u*30)}-${Math.round(u*50)}% player win rate`,confidence:n})):u>.15?o.push({description:"Reduce enemy CR by 1 level",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}):o.push({description:"Reduce enemy CR by 1 or remove one enemy ability",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),r.totalPlayerDeaths/r.totalRuns>2&&o.push({description:"Consider removing legendary actions or reducing legendary resistance count",expectedImpact:"+5-15% player win rate",confidence:n})}else if(l){const u=e-t.max;u>.15?(o.push({description:"Increase enemy CR by 1-2 levels",expectedImpact:`-${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),o.push({description:"Add 1-2 additional enemies",expectedImpact:`-${Math.round(u*30)}-${Math.round(u*50)}% player win rate`,confidence:n})):o.push({description:"Increase enemy CR by 1 level or add one more enemy",expectedImpact:`-${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n})}else r.averagePlayerHPPercentRemaining>80&&e>t.min+.05?o.push({description:"Encounter is well-balanced. Consider increasing difficulty slightly if players are ending with high HP.",expectedImpact:"-5-10% player win rate",confidence:n}):r.averagePlayerHPPercentRemaining<30&&e<t.max-.05?o.push({description:"Encounter is balanced but punishing. Consider a small reduction in enemy damage if players are consistently low on HP.",expectedImpact:"+5-10% player HP remaining",confidence:n}):o.push({description:"Encounter is well-balanced for the intended difficulty. No changes needed.",expectedImpact:"None — encounter is within target range",confidence:n});return o}}class oo{sweep(e,t,a,i){const n=this.generateValues(a.range),r=n.length,o=[];let l=!1;for(let c=0;c<r;c++){if(a.abortSignal?.aborted){l=!0;break}const u=n[c];try{const{modifiedPlayers:d,modifiedEnemies:h}=this.applyParameter(e,t,a.variable,u);if(d.length===0||h.length===0){o.push({parameterValue:u,playerWinRate:0,averageRounds:0,averageHPRemaining:0,totalPlayerDeaths:0,totalEnemyDeaths:0,medianRounds:0}),i?.(c+1,r);continue}const p=`${a.baseSeed??"sweep"}-${u}`,m=new pt().run(d,h,{runCount:a.simulationsPerPoint,baseSeed:p,aiConfig:a.aiConfig,combatConfig:a.combatConfig,collectDetailedLogs:!1,abortSignal:a.abortSignal});m.wasCancelled?(l=!0,o.push(this.summarizeToPoint(u,m.summary))):o.push(this.summarizeToPoint(u,m.summary))}catch{o.push({parameterValue:u,playerWinRate:0,averageRounds:0,averageHPRemaining:0,totalPlayerDeaths:0,totalEnemyDeaths:0,medianRounds:0})}i?.(c+1,r)}return{variable:a.variable,range:a.range,simulationsPerPoint:a.simulationsPerPoint,dataPoints:o,wasCancelled:l}}generateValues(e){const t=[];for(let a=e.min;a<=e.max+e.step*.5;a+=e.step){const i=Math.round(a*1e3)/1e3;if(i>e.max+e.step*.01)break;t.push(i)}return t}applyParameter(e,t,a,i){switch(a){case"cr":return this.applyCR(e,t,i);case"enemyCount":return this.applyEnemyCount(e,t,i);case"partyLevel":return this.applyPartyLevel(e,t,i);case"difficultyMultiplier":return this.applyDifficultyMultiplier(e,t,i);case"rarity":return this.applyRarity(e,t,i);case"hpLevel":return this.applyStatLevel(e,t,{hpLevel:i});case"attackLevel":return this.applyStatLevel(e,t,{attackLevel:i});case"defenseLevel":return this.applyStatLevel(e,t,{defenseLevel:i});default:return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(t,1)}}}applyCR(e,t,a){const i={...t,cr:a};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}applyEnemyCount(e,t,a){const i=Math.max(1,Math.round(a));return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(t,i)}}applyPartyLevel(e,t,a){const i=Math.max(1,Math.round(a));return{modifiedPlayers:e.map((r,o)=>({...r,level:i,hp:{...r.hp,current:Math.round(r.hp.current/Math.max(1,r.level)*i),max:Math.round(r.hp.max/Math.max(1,r.level)*i)}})),modifiedEnemies:this.generateEnemies(t,1)}}applyDifficultyMultiplier(e,t,a){const i={...t,difficultyMultiplier:a};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}applyRarity(e,t,a){const i=["common","uncommon","elite","boss"],n=Math.max(0,Math.min(i.length-1,Math.round(a))),r={...t,rarity:i[n]};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(r,1)}}applyStatLevel(e,t,a){const i={...t,statLevels:{...t.statLevels,...a}};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}generateEnemies(e,t){const a=[],i=e.templateId??e.category??"enemy";for(let n=0;n<t;n++){const r=`${i}-sweep-${n}`,o=Ke.generate({seed:r,cr:e.cr,rarity:e.rarity,category:e.category,archetype:e.archetype,templateId:e.templateId,statLevels:e.statLevels,difficultyMultiplier:e.difficultyMultiplier});a.push(o)}return a}summarizeToPoint(e,t){return{parameterValue:e,playerWinRate:t.playerWinRate,averageRounds:t.averageRounds,averageHPRemaining:t.averagePlayerHPPercentRemaining,totalPlayerDeaths:t.totalPlayerDeaths,totalEnemyDeaths:t.totalEnemyDeaths,medianRounds:t.medianRounds}}}class lo{compare(e,t,a){const i=e.label??"Config A",n=t.label??"Config B",r=this.runSimulations(e,a,`${a.baseSeed}-A`,i,a.onProgress),o=this.runSimulations(t,a,`${a.baseSeed}-B`,n,a.onProgress),l=this.calculateDeltas(r.summary,o.summary),c=this.calculateCombatantDeltas(r.perCombatantMetrics,o.perCombatantMetrics),u=a.significanceThreshold??.05,d=this.testSignificance(r.summary,o.summary,u),h=r.wasCancelled||o.wasCancelled;return{labelA:i,labelB:n,resultsA:r,resultsB:o,summaryA:r.summary,summaryB:o.summary,deltas:l,combatantDeltas:c,winRateSignificance:d,wasCancelled:h}}runSimulations(e,t,a,i,n){return new pt().run(e.players,e.enemies,{runCount:t.runCount,baseSeed:a,aiConfig:t.aiConfig,combatConfig:e.combatConfig??t.combatConfig,collectDetailedLogs:!1,abortSignal:t.abortSignal,onProgress:n?(o,l)=>n(o,l,i):void 0})}calculateDeltas(e,t){return{winRateDelta:this.round(e.playerWinRate-t.playerWinRate),averageRoundsDelta:this.round(e.averageRounds-t.averageRounds),averageHPRemainingDelta:this.round(e.averagePlayerHPPercentRemaining-t.averagePlayerHPPercentRemaining),totalPlayerDeathsDelta:e.totalPlayerDeaths-t.totalPlayerDeaths,totalEnemyDeathsDelta:e.totalEnemyDeaths-t.totalEnemyDeaths,medianRoundsDelta:e.medianRounds-t.medianRounds}}calculateCombatantDeltas(e,t){const a=[],i=this.getMetricsBySide(e,"player"),n=this.getMetricsBySide(t,"player"),r=this.getMetricsBySide(e,"enemy"),o=this.getMetricsBySide(t,"enemy"),l=Math.min(i.length,n.length);for(let u=0;u<l;u++)a.push(this.makeCombatantDelta(i[u],n[u]));for(let u=l;u<i.length;u++)a.push(this.makeCombatantDelta(i[u],null));for(let u=l;u<n.length;u++)a.push(this.makeCombatantDelta(null,n[u]));const c=Math.min(r.length,o.length);for(let u=0;u<c;u++)a.push(this.makeCombatantDelta(r[u],o[u]));for(let u=c;u<r.length;u++)a.push(this.makeCombatantDelta(r[u],null));for(let u=c;u<o.length;u++)a.push(this.makeCombatantDelta(null,o[u]));return a}getMetricsBySide(e,t){const a=[];for(const i of e.values())i.side===t&&a.push(i);return a}makeCombatantDelta(e,t){return e&&t?{name:e.name,side:e.side,dprDelta:this.round(e.averageDamagePerRound-t.averageDamagePerRound),damageDealtDelta:this.round(e.averageTotalDamageDealt-t.averageTotalDamageDealt),damageTakenDelta:this.round(e.averageTotalDamageTaken-t.averageTotalDamageTaken),survivalRateDelta:this.round(e.survivalRate-t.survivalRate),killRateDelta:this.round(e.killRate-t.killRate),criticalHitRateDelta:this.round(e.criticalHitRate-t.criticalHitRate),healingDoneDelta:this.round(e.averageHealingDone-t.averageHealingDone)}:e?{name:`${e.name} (only in A)`,side:e.side,dprDelta:e.averageDamagePerRound,damageDealtDelta:e.averageTotalDamageDealt,damageTakenDelta:e.averageTotalDamageTaken,survivalRateDelta:e.survivalRate,killRateDelta:e.killRate,criticalHitRateDelta:e.criticalHitRate,healingDoneDelta:e.averageHealingDone}:{name:`${t.name} (only in B)`,side:t.side,dprDelta:-t.averageDamagePerRound,damageDealtDelta:-t.averageTotalDamageDealt,damageTakenDelta:-t.averageTotalDamageTaken,survivalRateDelta:-t.survivalRate,killRateDelta:-t.killRate,criticalHitRateDelta:-t.criticalHitRate,healingDoneDelta:-t.averageHealingDone}}testSignificance(e,t,a){const i=Math.min(e.totalRuns,t.totalRuns);if(i===0)return{isSignificant:!1,pValue:1,threshold:a,interpretation:"No simulation data available"};const n=e.playerWinRate,r=t.playerWinRate,o=Math.abs(n-r);if(o===0)return{isSignificant:!1,pValue:1,threshold:a,interpretation:"No difference in win rates between configurations"};const l=n*(1-n)/i,c=r*(1-r)/i,u=Math.sqrt(l+c);if(u===0)return{isSignificant:o>0,pValue:o>0?0:1,threshold:a,interpretation:o>0?"One configuration wins all runs — difference is definitive":"Both configurations have identical outcomes"};const d=o/u,h=this.twoTailedPValue(d),p=h<a;let g;if(p){const m=n>r?"A":"B",f=(o*100).toFixed(1);g=`Config ${m} has a statistically significant ${f}% higher win rate (p=${h.toFixed(4)}, n=${i})`}else g=`The ${(o*100).toFixed(1)}% win rate difference is not statistically significant (p=${h.toFixed(4)}, n=${i}). More runs may be needed to detect a real difference.`;return{isSignificant:p,pValue:h,threshold:a,interpretation:g}}twoTailedPValue(e){const t=this.normalCDF(Math.abs(e));return Math.max(0,2*(1-t))}normalCDF(e){const t=.254829592,a=-.284496736,i=1.421413741,n=-1.453152027,r=1.061405429,o=.3275911,l=e<0?-1:1;e=Math.abs(e)/Math.SQRT2;const c=1/(1+o*e),u=1-((((r*c+n)*c+i)*c+a)*c+t)*c*Math.exp(-e*e);return .5*(1+l*u)}round(e){return Math.round(e*1e4)/1e4}}const oe=class oe{suggest(e,t,a,i){const{aiConfig:n,combatConfig:r,baseSeed:o="difficulty-calc",simulationsPerProbe:l=oe.DEFAULT_SIMULATIONS_PER_PROBE,maxIterations:c=oe.DEFAULT_MAX_ITERATIONS,enemyCount:u=1,abortSignal:d,onProgress:h}=i,p=[];let g=0;const m=e.map(H=>H.level),f=Wt(m,a),v=ot(u),w=f/v,E=Ta(w);let I=oe.CR_FLOOR,D=oe.CR_CEILING,x=Math.max(I,Math.min(D,E));const N=Pa[a];let $=!1,F=null,te=!1;for(let H=0;H<c;H++){if(d?.aborted){te=!0;break}h?.(H+1,c,x);const P=this.runProbe(e,t,x,u,n,r,l,o,H);g+=l;const L={cr:x,winRate:P.playerWinRate,totalRuns:P.totalRuns,averageRounds:P.averageRounds,averageHPRemaining:P.averagePlayerHPPercentRemaining};p.push(L);const _e=(N.min+N.max)/2;if((!F||Math.abs(L.winRate-_e)<Math.abs(F.winRate-_e))&&(F=L),L.winRate>=N.min&&L.winRate<=N.max){$=!0;break}L.winRate>N.max?I=x:D=x;const ge=(I+D)/2;if(Math.abs(ge-x)<oe.CR_STEP_MIN){if(L.winRate>N.max?x=Math.min(oe.CR_CEILING,x+oe.CR_STEP_MIN):x=Math.max(oe.CR_FLOOR,x-oe.CR_STEP_MIN),x===p[p.length-1].cr)break}else x=ge}const Q=F?.cr??x,X=this.generateEnemy(t,Q,o),de=this.calculateMarginOfError(F?.totalRuns??l),ee=this.formatConfidenceInterval(F?.winRate??0,de);return{targetDifficulty:a,recommendedCR:this.roundCR(Q),winRate:F?.winRate??0,expectedWinRateRange:N,confidenceInterval:ee,marginOfError:de,converged:$,totalSimulationsRun:g,iterationsUsed:p.length,probes:p,initialCREstimate:this.roundCR(E),suggestedEnemy:X,wasCancelled:te}}runProbe(e,t,a,i,n,r,o,l,c){const u=this.generateEnemies(t,a,i,l),d=new pt;try{return d.run(e,u,{runCount:o,baseSeed:`${l}-probe-${c}`,aiConfig:n,combatConfig:r,collectDetailedLogs:!1}).summary}catch{return{totalRuns:o,playerWins:0,enemyWins:o,draws:0,playerWinRate:0,averageRounds:0,medianRounds:0,averageRoundsOnWin:0,averageRoundsOnLoss:0,averagePlayerHPPercentRemaining:0,totalPlayerDeaths:o*e.length,averageRoundsPerPlayerDeath:0,totalEnemyDeaths:0,averageRoundsPerEnemyDeath:0}}}generateEnemies(e,t,a,i){const n=[];for(let r=0;r<a;r++)n.push(this.generateEnemy(e,t,`${i}-enemy-${r}`));return n}generateEnemy(e,t,a){return Ke.generate({seed:a,cr:t,rarity:e.rarity??"elite",category:e.category??"humanoid",archetype:e.archetype??"brute",templateId:e.templateId,statLevels:e.statLevels,difficultyMultiplier:e.difficultyMultiplier})}calculateMarginOfError(e){return e<=0?1:1.96*Math.sqrt(.5*.5/e)}formatConfidenceInterval(e,t){const a=Math.round(e*100),i=Math.round(t*100);return`${a}% ± ${i}%`}roundCR(e){return e<=0?0:e<.25?.125:e<.375?.25:e<.75?.5:Math.round(e)}};oe.DEFAULT_SIMULATIONS_PER_PROBE=200,oe.DEFAULT_MAX_ITERATIONS=10,oe.CR_STEP_MIN=.25,oe.CR_FLOOR=.125,oe.CR_CEILING=30;let ca=oe;const co=s=>s*5,Me={baseXP:{perfect:10,great:7,good:5,ok:2,miss:0,wrongKey:0},xpRatio:.1,combo:{enabled:!0,cap:5,okBreaksCombo:!0,endBonus:{enabled:!0,formula:co}},groove:{perHitMultiplier:!1,perHitScale:1,endBonus:{enabled:!0,maxStreakWeight:.5,avgHotnessWeight:.5,durationWeight:.25}},maxMultiplier:5};function uo(s){const e=Me.combo;return s?{enabled:s.enabled??e.enabled,cap:s.cap??e.cap,formula:s.formula??e.formula,okBreaksCombo:s.okBreaksCombo??e.okBreaksCombo,endBonus:{...e.endBonus,...s.endBonus}}:e}function ho(s){const e=Me.groove;return s?{perHitMultiplier:s.perHitMultiplier??e.perHitMultiplier,perHitScale:s.perHitScale??e.perHitScale,endBonus:{...e.endBonus,...s.endBonus}}:e}function Pt(s){return s?{baseXP:{...Me.baseXP,...s.baseXP},xpRatio:s.xpRatio??Me.xpRatio,combo:uo(s.combo),groove:ho(s.groove),maxMultiplier:s.maxMultiplier??Me.maxMultiplier}:Me}function po(s,e=Me.combo.okBreaksCombo){return s==="miss"||s==="wrongKey"?!0:s==="ok"?e:!1}function b(s,e,t){function a(o,l){if(o._zod||Object.defineProperty(o,"_zod",{value:{def:l,constr:r,traits:new Set},enumerable:!1}),o._zod.traits.has(s))return;o._zod.traits.add(s),e(o,l);const c=r.prototype,u=Object.keys(c);for(let d=0;d<u.length;d++){const h=u[d];h in o||(o[h]=c[h].bind(o))}}const i=t?.Parent??Object;class n extends i{}Object.defineProperty(n,"name",{value:s});function r(o){var l;const c=t?.Parent?new n:this;a(c,o),(l=c._zod).deferred??(l.deferred=[]);for(const u of c._zod.deferred)u();return c}return Object.defineProperty(r,"init",{value:a}),Object.defineProperty(r,Symbol.hasInstance,{value:o=>t?.Parent&&o instanceof t.Parent?!0:o?._zod?.traits?.has(s)}),Object.defineProperty(r,"name",{value:s}),r}class Ye extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}class Ws extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name="ZodEncodeError"}}const zs={};function xe(s){return zs}function qs(s){const e=Object.values(s).filter(a=>typeof a=="number");return Object.entries(s).filter(([a,i])=>e.indexOf(+a)===-1).map(([a,i])=>i)}function ua(s,e){return typeof e=="bigint"?e.toString():e}function xa(s){return{get value(){{const e=s();return Object.defineProperty(this,"value",{value:e}),e}}}}function Na(s){return s==null}function $a(s){const e=s.startsWith("^")?1:0,t=s.endsWith("$")?s.length-1:s.length;return s.slice(e,t)}function mo(s,e){const t=(s.toString().split(".")[1]||"").length,a=e.toString();let i=(a.split(".")[1]||"").length;if(i===0&&/\d?e-\d?/.test(a)){const l=a.match(/\d?e-(\d?)/);l?.[1]&&(i=Number.parseInt(l[1]))}const n=t>i?t:i,r=Number.parseInt(s.toFixed(n).replace(".","")),o=Number.parseInt(e.toFixed(n).replace(".",""));return r%o/10**n}const Ni=Symbol("evaluating");function B(s,e,t){let a;Object.defineProperty(s,e,{get(){if(a!==Ni)return a===void 0&&(a=Ni,a=t()),a},set(i){Object.defineProperty(s,e,{value:i})},configurable:!0})}function He(s,e,t){Object.defineProperty(s,e,{value:t,writable:!0,enumerable:!0,configurable:!0})}function $e(...s){const e={};for(const t of s){const a=Object.getOwnPropertyDescriptors(t);Object.assign(e,a)}return Object.defineProperties({},e)}function $i(s){return JSON.stringify(s)}function go(s){return s.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}const Us="captureStackTrace"in Error?Error.captureStackTrace:(...s)=>{};function xt(s){return typeof s=="object"&&s!==null&&!Array.isArray(s)}const fo=xa(()=>{if(typeof navigator<"u"&&navigator?.userAgent?.includes("Cloudflare"))return!1;try{const s=Function;return new s(""),!0}catch{return!1}});function Je(s){if(xt(s)===!1)return!1;const e=s.constructor;if(e===void 0||typeof e!="function")return!0;const t=e.prototype;return!(xt(t)===!1||Object.prototype.hasOwnProperty.call(t,"isPrototypeOf")===!1)}function Gs(s){return Je(s)?{...s}:Array.isArray(s)?[...s]:s}const yo=new Set(["string","number","symbol"]);function qt(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Le(s,e,t){const a=new s._zod.constr(e??s._zod.def);return(!e||t?.parent)&&(a._zod.parent=s),a}function M(s){const e=s;if(!e)return{};if(typeof e=="string")return{error:()=>e};if(e?.message!==void 0){if(e?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");e.error=e.message}return delete e.message,typeof e.error=="string"?{...e,error:()=>e.error}:e}function vo(s){return Object.keys(s).filter(e=>s[e]._zod.optin==="optional"&&s[e]._zod.optout==="optional")}const bo={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function wo(s,e){const t=s._zod.def,a=t.checks;if(a&&a.length>0)throw new Error(".pick() cannot be used on object schemas containing refinements");const n=$e(s._zod.def,{get shape(){const r={};for(const o in e){if(!(o in t.shape))throw new Error(`Unrecognized key: "${o}"`);e[o]&&(r[o]=t.shape[o])}return He(this,"shape",r),r},checks:[]});return Le(s,n)}function So(s,e){const t=s._zod.def,a=t.checks;if(a&&a.length>0)throw new Error(".omit() cannot be used on object schemas containing refinements");const n=$e(s._zod.def,{get shape(){const r={...s._zod.def.shape};for(const o in e){if(!(o in t.shape))throw new Error(`Unrecognized key: "${o}"`);e[o]&&delete r[o]}return He(this,"shape",r),r},checks:[]});return Le(s,n)}function _o(s,e){if(!Je(e))throw new Error("Invalid input to extend: expected a plain object");const t=s._zod.def.checks;if(t&&t.length>0){const n=s._zod.def.shape;for(const r in e)if(Object.getOwnPropertyDescriptor(n,r)!==void 0)throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}const i=$e(s._zod.def,{get shape(){const n={...s._zod.def.shape,...e};return He(this,"shape",n),n}});return Le(s,i)}function Ao(s,e){if(!Je(e))throw new Error("Invalid input to safeExtend: expected a plain object");const t=$e(s._zod.def,{get shape(){const a={...s._zod.def.shape,...e};return He(this,"shape",a),a}});return Le(s,t)}function ko(s,e){const t=$e(s._zod.def,{get shape(){const a={...s._zod.def.shape,...e._zod.def.shape};return He(this,"shape",a),a},get catchall(){return e._zod.def.catchall},checks:[]});return Le(s,t)}function To(s,e,t){const i=e._zod.def.checks;if(i&&i.length>0)throw new Error(".partial() cannot be used on object schemas containing refinements");const r=$e(e._zod.def,{get shape(){const o=e._zod.def.shape,l={...o};if(t)for(const c in t){if(!(c in o))throw new Error(`Unrecognized key: "${c}"`);t[c]&&(l[c]=s?new s({type:"optional",innerType:o[c]}):o[c])}else for(const c in o)l[c]=s?new s({type:"optional",innerType:o[c]}):o[c];return He(this,"shape",l),l},checks:[]});return Le(e,r)}function Co(s,e,t){const a=$e(e._zod.def,{get shape(){const i=e._zod.def.shape,n={...i};if(t)for(const r in t){if(!(r in n))throw new Error(`Unrecognized key: "${r}"`);t[r]&&(n[r]=new s({type:"nonoptional",innerType:i[r]}))}else for(const r in i)n[r]=new s({type:"nonoptional",innerType:i[r]});return He(this,"shape",n),n}});return Le(e,a)}function je(s,e=0){if(s.aborted===!0)return!0;for(let t=e;t<s.issues.length;t++)if(s.issues[t]?.continue!==!0)return!0;return!1}function Ve(s,e){return e.map(t=>{var a;return(a=t).path??(a.path=[]),t.path.unshift(s),t})}function At(s){return typeof s=="string"?s:s?.message}function Ne(s,e,t){const a={...s,path:s.path??[]};if(!s.message){const i=At(s.inst?._zod.def?.error?.(s))??At(e?.error?.(s))??At(t.customError?.(s))??At(t.localeError?.(s))??"Invalid input";a.message=i}return delete a.inst,delete a.continue,e?.reportInput||delete a.input,a}function La(s){return Array.isArray(s)?"array":typeof s=="string"?"string":"unknown"}function ut(...s){const[e,t,a]=s;return typeof e=="string"?{message:e,code:"custom",input:t,inst:a}:{...e}}const js=(s,e)=>{s.name="$ZodError",Object.defineProperty(s,"_zod",{value:s._zod,enumerable:!1}),Object.defineProperty(s,"issues",{value:e,enumerable:!1}),s.message=JSON.stringify(e,ua,2),Object.defineProperty(s,"toString",{value:()=>s.message,enumerable:!1})},Vs=b("$ZodError",js),Ys=b("$ZodError",js,{Parent:Error});function Io(s,e=t=>t.message){const t={},a=[];for(const i of s.issues)i.path.length>0?(t[i.path[0]]=t[i.path[0]]||[],t[i.path[0]].push(e(i))):a.push(e(i));return{formErrors:a,fieldErrors:t}}function Eo(s,e=t=>t.message){const t={_errors:[]},a=i=>{for(const n of i.issues)if(n.code==="invalid_union"&&n.errors.length)n.errors.map(r=>a({issues:r}));else if(n.code==="invalid_key")a({issues:n.issues});else if(n.code==="invalid_element")a({issues:n.issues});else if(n.path.length===0)t._errors.push(e(n));else{let r=t,o=0;for(;o<n.path.length;){const l=n.path[o];o===n.path.length-1?(r[l]=r[l]||{_errors:[]},r[l]._errors.push(e(n))):r[l]=r[l]||{_errors:[]},r=r[l],o++}}};return a(s),t}const Oa=s=>(e,t,a,i)=>{const n=a?Object.assign(a,{async:!1}):{async:!1},r=e._zod.run({value:t,issues:[]},n);if(r instanceof Promise)throw new Ye;if(r.issues.length){const o=new(i?.Err??s)(r.issues.map(l=>Ne(l,n,xe())));throw Us(o,i?.callee),o}return r.value},Ba=s=>async(e,t,a,i)=>{const n=a?Object.assign(a,{async:!0}):{async:!0};let r=e._zod.run({value:t,issues:[]},n);if(r instanceof Promise&&(r=await r),r.issues.length){const o=new(i?.Err??s)(r.issues.map(l=>Ne(l,n,xe())));throw Us(o,i?.callee),o}return r.value},Ut=s=>(e,t,a)=>{const i=a?{...a,async:!1}:{async:!1},n=e._zod.run({value:t,issues:[]},i);if(n instanceof Promise)throw new Ye;return n.issues.length?{success:!1,error:new(s??Vs)(n.issues.map(r=>Ne(r,i,xe())))}:{success:!0,data:n.value}},Mo=Ut(Ys),Gt=s=>async(e,t,a)=>{const i=a?Object.assign(a,{async:!0}):{async:!0};let n=e._zod.run({value:t,issues:[]},i);return n instanceof Promise&&(n=await n),n.issues.length?{success:!1,error:new s(n.issues.map(r=>Ne(r,i,xe())))}:{success:!0,data:n.value}},Do=Gt(Ys),Ro=s=>(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Oa(s)(e,t,i)},Po=s=>(e,t,a)=>Oa(s)(e,t,a),xo=s=>async(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Ba(s)(e,t,i)},No=s=>async(e,t,a)=>Ba(s)(e,t,a),$o=s=>(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Ut(s)(e,t,i)},Lo=s=>(e,t,a)=>Ut(s)(e,t,a),Oo=s=>async(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Gt(s)(e,t,i)},Bo=s=>async(e,t,a)=>Gt(s)(e,t,a),Fo=/^[cC][^\s-]{8,}$/,Ho=/^[0-9a-z]+$/,Wo=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,zo=/^[0-9a-vA-V]{20}$/,qo=/^[A-Za-z0-9]{27}$/,Uo=/^[a-zA-Z0-9_-]{21}$/,Go=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,jo=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,Li=s=>s?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${s}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,Vo=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,Yo="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";function Zo(){return new RegExp(Yo,"u")}const Xo=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Ko=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,Jo=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,Qo=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,el=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Zs=/^[A-Za-z0-9_-]*$/,tl=/^\+[1-9]\d{6,14}$/,Xs="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",al=new RegExp(`^${Xs}$`);function Ks(s){const e="(?:[01]\\d|2[0-3]):[0-5]\\d";return typeof s.precision=="number"?s.precision===-1?`${e}`:s.precision===0?`${e}:[0-5]\\d`:`${e}:[0-5]\\d\\.\\d{${s.precision}}`:`${e}(?::[0-5]\\d(?:\\.\\d+)?)?`}function il(s){return new RegExp(`^${Ks(s)}$`)}function sl(s){const e=Ks({precision:s.precision}),t=["Z"];s.local&&t.push(""),s.offset&&t.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");const a=`${e}(?:${t.join("|")})`;return new RegExp(`^${Xs}T(?:${a})$`)}const nl=s=>{const e=s?`[\\s\\S]{${s?.minimum??0},${s?.maximum??""}}`:"[\\s\\S]*";return new RegExp(`^${e}$`)},rl=/^-?\d+$/,Js=/^-?\d+(?:\.\d+)?$/,ol=/^(?:true|false)$/i,ll=/^[^A-Z]*$/,cl=/^[^a-z]*$/,me=b("$ZodCheck",(s,e)=>{var t;s._zod??(s._zod={}),s._zod.def=e,(t=s._zod).onattach??(t.onattach=[])}),Qs={number:"number",bigint:"bigint",object:"date"},en=b("$ZodCheckLessThan",(s,e)=>{me.init(s,e);const t=Qs[typeof e.value];s._zod.onattach.push(a=>{const i=a._zod.bag,n=(e.inclusive?i.maximum:i.exclusiveMaximum)??Number.POSITIVE_INFINITY;e.value<n&&(e.inclusive?i.maximum=e.value:i.exclusiveMaximum=e.value)}),s._zod.check=a=>{(e.inclusive?a.value<=e.value:a.value<e.value)||a.issues.push({origin:t,code:"too_big",maximum:typeof e.value=="object"?e.value.getTime():e.value,input:a.value,inclusive:e.inclusive,inst:s,continue:!e.abort})}}),tn=b("$ZodCheckGreaterThan",(s,e)=>{me.init(s,e);const t=Qs[typeof e.value];s._zod.onattach.push(a=>{const i=a._zod.bag,n=(e.inclusive?i.minimum:i.exclusiveMinimum)??Number.NEGATIVE_INFINITY;e.value>n&&(e.inclusive?i.minimum=e.value:i.exclusiveMinimum=e.value)}),s._zod.check=a=>{(e.inclusive?a.value>=e.value:a.value>e.value)||a.issues.push({origin:t,code:"too_small",minimum:typeof e.value=="object"?e.value.getTime():e.value,input:a.value,inclusive:e.inclusive,inst:s,continue:!e.abort})}}),ul=b("$ZodCheckMultipleOf",(s,e)=>{me.init(s,e),s._zod.onattach.push(t=>{var a;(a=t._zod.bag).multipleOf??(a.multipleOf=e.value)}),s._zod.check=t=>{if(typeof t.value!=typeof e.value)throw new Error("Cannot mix number and bigint in multiple_of check.");(typeof t.value=="bigint"?t.value%e.value===BigInt(0):mo(t.value,e.value)===0)||t.issues.push({origin:typeof t.value,code:"not_multiple_of",divisor:e.value,input:t.value,inst:s,continue:!e.abort})}}),dl=b("$ZodCheckNumberFormat",(s,e)=>{me.init(s,e),e.format=e.format||"float64";const t=e.format?.includes("int"),a=t?"int":"number",[i,n]=bo[e.format];s._zod.onattach.push(r=>{const o=r._zod.bag;o.format=e.format,o.minimum=i,o.maximum=n,t&&(o.pattern=rl)}),s._zod.check=r=>{const o=r.value;if(t){if(!Number.isInteger(o)){r.issues.push({expected:a,format:e.format,code:"invalid_type",continue:!1,input:o,inst:s});return}if(!Number.isSafeInteger(o)){o>0?r.issues.push({input:o,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:s,origin:a,inclusive:!0,continue:!e.abort}):r.issues.push({input:o,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:s,origin:a,inclusive:!0,continue:!e.abort});return}}o<i&&r.issues.push({origin:"number",input:o,code:"too_small",minimum:i,inclusive:!0,inst:s,continue:!e.abort}),o>n&&r.issues.push({origin:"number",input:o,code:"too_big",maximum:n,inclusive:!0,inst:s,continue:!e.abort})}}),hl=b("$ZodCheckMaxLength",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag.maximum??Number.POSITIVE_INFINITY;e.maximum<i&&(a._zod.bag.maximum=e.maximum)}),s._zod.check=a=>{const i=a.value;if(i.length<=e.maximum)return;const r=La(i);a.issues.push({origin:r,code:"too_big",maximum:e.maximum,inclusive:!0,input:i,inst:s,continue:!e.abort})}}),pl=b("$ZodCheckMinLength",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag.minimum??Number.NEGATIVE_INFINITY;e.minimum>i&&(a._zod.bag.minimum=e.minimum)}),s._zod.check=a=>{const i=a.value;if(i.length>=e.minimum)return;const r=La(i);a.issues.push({origin:r,code:"too_small",minimum:e.minimum,inclusive:!0,input:i,inst:s,continue:!e.abort})}}),ml=b("$ZodCheckLengthEquals",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag;i.minimum=e.length,i.maximum=e.length,i.length=e.length}),s._zod.check=a=>{const i=a.value,n=i.length;if(n===e.length)return;const r=La(i),o=n>e.length;a.issues.push({origin:r,...o?{code:"too_big",maximum:e.length}:{code:"too_small",minimum:e.length},inclusive:!0,exact:!0,input:a.value,inst:s,continue:!e.abort})}}),jt=b("$ZodCheckStringFormat",(s,e)=>{var t,a;me.init(s,e),s._zod.onattach.push(i=>{const n=i._zod.bag;n.format=e.format,e.pattern&&(n.patterns??(n.patterns=new Set),n.patterns.add(e.pattern))}),e.pattern?(t=s._zod).check??(t.check=i=>{e.pattern.lastIndex=0,!e.pattern.test(i.value)&&i.issues.push({origin:"string",code:"invalid_format",format:e.format,input:i.value,...e.pattern?{pattern:e.pattern.toString()}:{},inst:s,continue:!e.abort})}):(a=s._zod).check??(a.check=()=>{})}),gl=b("$ZodCheckRegex",(s,e)=>{jt.init(s,e),s._zod.check=t=>{e.pattern.lastIndex=0,!e.pattern.test(t.value)&&t.issues.push({origin:"string",code:"invalid_format",format:"regex",input:t.value,pattern:e.pattern.toString(),inst:s,continue:!e.abort})}}),fl=b("$ZodCheckLowerCase",(s,e)=>{e.pattern??(e.pattern=ll),jt.init(s,e)}),yl=b("$ZodCheckUpperCase",(s,e)=>{e.pattern??(e.pattern=cl),jt.init(s,e)}),vl=b("$ZodCheckIncludes",(s,e)=>{me.init(s,e);const t=qt(e.includes),a=new RegExp(typeof e.position=="number"?`^.{${e.position}}${t}`:t);e.pattern=a,s._zod.onattach.push(i=>{const n=i._zod.bag;n.patterns??(n.patterns=new Set),n.patterns.add(a)}),s._zod.check=i=>{i.value.includes(e.includes,e.position)||i.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:e.includes,input:i.value,inst:s,continue:!e.abort})}}),bl=b("$ZodCheckStartsWith",(s,e)=>{me.init(s,e);const t=new RegExp(`^${qt(e.prefix)}.*`);e.pattern??(e.pattern=t),s._zod.onattach.push(a=>{const i=a._zod.bag;i.patterns??(i.patterns=new Set),i.patterns.add(t)}),s._zod.check=a=>{a.value.startsWith(e.prefix)||a.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:e.prefix,input:a.value,inst:s,continue:!e.abort})}}),wl=b("$ZodCheckEndsWith",(s,e)=>{me.init(s,e);const t=new RegExp(`.*${qt(e.suffix)}$`);e.pattern??(e.pattern=t),s._zod.onattach.push(a=>{const i=a._zod.bag;i.patterns??(i.patterns=new Set),i.patterns.add(t)}),s._zod.check=a=>{a.value.endsWith(e.suffix)||a.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:e.suffix,input:a.value,inst:s,continue:!e.abort})}}),Sl=b("$ZodCheckOverwrite",(s,e)=>{me.init(s,e),s._zod.check=t=>{t.value=e.tx(t.value)}});class _l{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),this.indent-=1}write(e){if(typeof e=="function"){e(this,{execution:"sync"}),e(this,{execution:"async"});return}const a=e.split(`
|
|
11
|
+
`)}`);const r=this.get(e),o=this.getDefaultIdentifierKey(e),l=Date.now();let c=0;this.imageOverrides.has(e)||this.imageOverrides.set(e,new Map);const u=this.imageOverrides.get(e);for(const d of r){const h=String(d[t]??""),p=a[h];if(p!==void 0){const g=d[o],m=u.get(g);let f,v;typeof p=="string"?(f=p,v=m?.image):(f=p.icon!==void 0?p.icon:m?.icon,v=p.image!==void 0?p.image:m?.image);const w={identifier:g,icon:f,image:v,appliedAt:l};u.set(g,w),c++}}return this.invalidateRegistryCache(e),c}getImageOverrides(){const e=new Map;for(const[t,a]of this.imageOverrides.entries())e.set(t,Array.from(a.values()));return e}getImageOverridesForCategory(e){const t=this.imageOverrides.get(e);return t?Array.from(t.values()):[]}restoreImageOverrides(e,t){if(!t||t.length===0)return;this.imageOverrides.has(e)||this.imageOverrides.set(e,new Map);const a=this.imageOverrides.get(e);for(const i of t)a.set(i.identifier,i);this.invalidateRegistryCache(e)}clearImageOverrides(e){this.imageOverrides.delete(e),this.invalidateRegistryCache(e)}clearAllImageOverrides(){this.imageOverrides.clear();const e=["spells","skills","classFeatures","racialTraits","equipment","races.data","classes.data"];for(const t of e)this.invalidateRegistryCache(t)}getDefaultIdentifierKey(e){return"name"}validateImageField(e,t){return Es(e)?null:`${t}: ${e} (URLs must start with: http://, https://, /, or assets/)`}validateImageFields(e){const t=[];for(const[a,i]of e){const n=this.validateImageField(a,i);n!==null&&t.push(n)}return t}}const Ur=Object.freeze(Object.defineProperty({__proto__:null,ExtensionManager:R},Symbol.toStringTag,{value:"Module"})),Ns={Human:{ability_bonuses:{STR:1,DEX:1,CON:1,INT:1,WIS:1,CHA:1},speed:30,traits:["Versatile","Extra Language"],subraces:["Calishite","Chondathan","Damaran","Illuskan","Mulan","Rashemi","Shou","Tethyrian","Turami"],description:"Humans are the most adaptable and ambitious people among the common races. They have widely varying tastes, morals, and customs in the many different lands they call home. Their drive and their innovative spirit help them push boundaries and explore new frontiers."},Elf:{ability_bonuses:{DEX:2},speed:30,traits:["Darkvision","Keen Senses","Fey Ancestry","Trance"],subraces:["High Elf","Wood Elf","Dark Elf (Drow)"],description:"Elves are a magical people of otherworldly grace, living in the world but apart from it. They possess an innate connection to the Feywild and the natural world, with lifespans that measure in centuries. Elves value art, music, and nature, and their society emphasizes individual freedom and creative expression."},Dwarf:{ability_bonuses:{CON:2},speed:25,traits:["Darkvision","Dwarven Resilience","Stonecunning"],subraces:["Hill Dwarf","Mountain Dwarf"],description:"Bold and hardy, dwarves are known as skilled warriors, miners, and workers of stone and metal. They stand well under 5 feet tall but are so broad and compact that they can weigh as much as a human. Dwarves value clan, tradition, and craftsmanship, often dwelling in mountains or underground kingdoms."},Halfling:{ability_bonuses:{DEX:2},speed:25,traits:["Lucky","Brave","Halfling Nimbleness"],subraces:["Lightfoot","Stout"],description:"The diminutive halflings survive in a world full of larger creatures by avoiding notice or, barring that, avoiding offense. They are comfortable and lucky, taking what life brings with good cheer. Standing about 3 feet tall, they appear relatively harmless and so have managed to survive for centuries in the shadow of empires and on the edges of wars."},Dragonborn:{ability_bonuses:{STR:2,CHA:1},speed:30,traits:["Draconic Ancestry","Breath Weapon","Damage Resistance"],subraces:["Chromatic","Metallic","Gem"],description:"Dragonborn look very much like dragons standing erect in humanoid form, though they lack wings or tails. Proud and noble, they carry the blood of ancient dragons within them, granting them a breath weapon and resistance to elemental damage. They value honor and clan above all else."},Gnome:{ability_bonuses:{INT:2},speed:25,traits:["Darkvision","Gnome Cunning"],subraces:["Forest Gnome","Rock Gnome"],description:"Gnomes are inventive and curious, with an innate connection to the Illusory and magical arts. Standing about 3 to 3.5 feet tall, they approach problems with enthusiasm and optimism. Their society prizes humor, clever inventions, and arcane scholarship, with many gnomes becoming accomplished wizards, engineers, or pranksters."},"Half-Elf":{ability_bonuses:{CHA:2},speed:30,traits:["Darkvision","Fey Ancestry","Skill Versatility"],description:"Half-elves combine the best qualities of their elven and human parents. They share the elves' grace and curiosity, along with the humans' drive and versatility. As natural diplomats and charismatic leaders, half-elves can fit into virtually any society, often serving as bridges between cultures."},"Half-Orc":{ability_bonuses:{STR:2,CON:1},speed:30,traits:["Darkvision","Relentless Endurance","Savage Attacks"],description:"Half-orcs inherit the physical prowess and cunning of their orcish ancestors, combined with the adaptability and ambition of their human heritage. They stand taller than most humans, with grayish skin, tusks, and faces that combine bestial and humanoid features. Half-orcs are often outcasts but prove themselves as ferocious warriors and loyal companions."},Tiefling:{ability_bonuses:{CHA:2,INT:1},speed:30,traits:["Darkvision","Hellish Resistance","Infernal Legacy"],description:"Tieflings are humanoids with infernal blood, often bearing horns, tails, and other fiendish features. Their ancestry grants them resistance to fire and innate magical abilities. Though often feared and distrusted due to their appearance, tieflings are as varied in morality and ambition as any other race, with many becoming powerful warlocks, sorcerers, or charismatic leaders."}},Gr=Ns,$s=Object.entries(Ns).map(([s,e])=>({race:s,...e}));let Xt=null,Kt=null;async function jr(s){try{Xt||(Kt||(Kt=Promise.resolve().then(()=>Ur)),Xt=(await Kt).ExtensionManager);const t=Xt.getInstance().get("races.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.race===s);if(a)return a}}catch{}}function zt(s){try{const t=R.getInstance().get("races.data");if(t&&Array.isArray(t)){const a=t.find(i=>i.race===s);if(a)return a}}catch{}}class re{constructor(){this.allClassFeaturesCache=null,this.classFeaturesIndex=null,this.allRacialTraitsCache=null,this.racialTraitsIndex=null,this.manager=R.getInstance()}static getInstance(){return re.instance||(re.instance=new re),re.instance}invalidateCache(){this.allClassFeaturesCache=null,this.classFeaturesIndex=null,this.allRacialTraitsCache=null,this.racialTraitsIndex=null}getAllClassFeaturesArray(){if(!this.allClassFeaturesCache){const e=this.manager.get("classFeatures");this.allClassFeaturesCache=e}return this.allClassFeaturesCache}getAllRacialTraitsArray(){if(!this.allRacialTraitsCache){const e=this.manager.getAllFromPrefix("racialTraits");this.allRacialTraitsCache=e}return this.allRacialTraitsCache}getClassFeatures(e,t){return this.getAllClassFeaturesArray().filter(i=>i.class===e&&i.level<=t)}getFeaturesForLevel(e,t){return this.getAllClassFeaturesArray().filter(i=>i.class===e&&i.level===t)}getClassFeaturesForLevel(e,t){return this.getFeaturesForLevel(e,t)}getClassFeatureById(e){return this.getAllClassFeaturesArray().find(a=>a.id===e)}getAllClassFeatures(){if(!this.classFeaturesIndex){this.classFeaturesIndex=new Map;const e=this.getAllClassFeaturesArray();for(const t of e)this.classFeaturesIndex.has(t.class)||this.classFeaturesIndex.set(t.class,[]),this.classFeaturesIndex.get(t.class).push(t)}return new Map(this.classFeaturesIndex)}getRacialTraits(e){return this.getAllRacialTraitsArray().filter(a=>a.race===e)}getBaseRacialTraits(e){return this.getAllRacialTraitsArray().filter(a=>a.race===e&&!a.subrace)}getRacialTraitsForSubrace(e,t){return this.getAllRacialTraitsArray().filter(i=>i.race===e&&(!i.subrace||i.subrace===t))}getSubraceTraits(e,t){return this.getAllRacialTraitsArray().filter(i=>i.race===e&&i.subrace===t)}getAvailableSubraces(e){const t=zt(e);if(t?.subraces&&t.subraces.length>0)return t.subraces;const a=this.getAllRacialTraitsArray(),i=new Set;for(const n of a)n.race===e&&n.subrace&&i.add(n.subrace);return Array.from(i).sort()}getRaceForSubrace(e){const t=this.getAllRacialTraitsArray();for(const a of t)if(a.subrace===e)return a.race}getRacialTraitById(e){return this.getAllRacialTraitsArray().find(a=>a.id===e)}getAllRacialTraits(){if(!this.racialTraitsIndex){this.racialTraitsIndex=new Map;const e=this.getAllRacialTraitsArray();for(const t of e)this.racialTraitsIndex.has(t.race)||this.racialTraitsIndex.set(t.race,[]),this.racialTraitsIndex.get(t.race).push(t)}return new Map(this.racialTraitsIndex)}validatePrerequisites(e,t){const a=[];if(!e.prerequisites)return{valid:!0};const i=e.prerequisites;if(i.level!==void 0&&t.level<i.level&&a.push(`Requires level ${i.level} (current: ${t.level})`),i.abilities)for(const[n,r]of Object.entries(i.abilities)){const o=t.ability_scores[n];(o===void 0||o<r)&&a.push(`Requires ${n} ${r} (current: ${o??0})`)}if(i.class!==void 0&&t.class!==i.class&&a.push(`Requires class ${i.class}`),i.race!==void 0&&t.race!==i.race&&a.push(`Requires race ${i.race} (current: ${t.race})`),i.subrace!==void 0&&(!t.subrace||t.subrace!==i.subrace)&&a.push(`Requires subrace ${i.subrace} (current: ${t.subrace||"none"})`),i.skills&&i.skills.length>0)for(const n of i.skills){const r=t.skills[n];r!=="proficient"&&r!=="expertise"&&a.push(`Requires proficiency in ${n}`)}if(i.spells&&i.spells.length>0){const n=t.spells?.known_spells||[],r=t.spells?.cantrips||[],o=[...n,...r];for(const l of i.spells)o.includes(l)||a.push(`Requires spell: ${l}`)}if(i.features&&i.features.length>0){const n=this.getCharacterFeatureIds(t);for(const r of i.features)if(!n.includes(r)){const l=this.getClassFeatureById(r)?.name||r;a.push(`Requires feature: ${l}`)}}return{valid:a.length===0,errors:a.length>0?a:void 0,unmet:a.length>0?a:void 0}}validateFeaturePrerequisites(e,t){return this.validatePrerequisites(e,t)}validateTraitPrerequisites(e,t){return this.validatePrerequisites(e,t)}getCharacterFeatureIds(e){return e.class_features||[]}canGainFeature(e,t){return this.validatePrerequisites(e,t).valid}meetsPrerequisites(e,t){return this.canGainFeature(e,t)}getRegisteredClasses(){const e=this.getAllClassFeaturesArray(),t=new Set;for(const a of e)t.add(a.class);return Array.from(t)}getRegisteredRaces(){const e=this.getAllRacialTraitsArray(),t=new Set;for(const a of e)t.add(a.race);return Array.from(t)}getQueryStats(){const e=this.getAllClassFeaturesArray(),t=new Set;for(const n of e)t.add(n.class);const a=this.getAllRacialTraitsArray(),i=new Set;for(const n of a)i.add(n.race);return{totalClassFeatures:e.length,totalRacialTraits:a.length,classesWithFeatures:t.size,racesWithTraits:i.size}}clearQueryCache(){this.manager.reset("classFeatures"),this.manager.reset("racialTraits"),this.invalidateCache()}isInitialized(){return this.manager.hasCustomData("racialTraits")||this.manager.getDefaults("racialTraits").length>0}static getEquipmentFeatures(e){const t=re.getInstance(),a=[],i=t.getAllClassFeaturesArray();for(const n of i)(n.tags?.includes("equipment")||n.tags?.includes("item")||n.source==="custom")&&a.push(n);return a}static isValidEquipmentFeature(e){return!!re.getInstance().getClassFeatureById(e)}exportRacialTraits(){const e={},t=this.getAllRacialTraits();for(const[a,i]of t.entries())e[a]=i;return e}}function Vr(){return re.getInstance()}const Yr={greataxe:"Greataxe",longsword:"Longsword","brute-mace":"Mace",handaxe:"Handaxe",longbow:"Longbow","light-crossbow":"Light Crossbow",shortsword:"Shortsword",dagger:"Dagger",quarterstaff:"Quarterstaff","support-mace":"Mace","support-dagger":"Dagger","leather-armor":"Leather Armor","scale-mail":"Scale Mail","chain-mail":"Chain Mail","plate-armor":"Plate Armor",shield:"Shield"},qe=[{id:"greataxe",name:"Greataxe",type:"weapon",archetypes:["brute"],rarities:["uncommon","elite","boss"],damage:"1d12",properties:["two-handed"]},{id:"longsword",name:"Longsword",type:"weapon",archetypes:["brute"],rarities:["common","uncommon","elite","boss"],damage:"1d8",properties:["versatile"]},{id:"brute-mace",name:"Mace",type:"weapon",archetypes:["brute"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile"]},{id:"handaxe",name:"Handaxe",type:"weapon",archetypes:["brute"],rarities:["common","uncommon"],damage:"1d6",properties:["light","thrown"]},{id:"longbow",name:"Longbow",type:"weapon",archetypes:["archer"],rarities:["uncommon","elite","boss"],damage:"1d8",properties:["ammunition","two-handed","heavy"]},{id:"light-crossbow",name:"Light Crossbow",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite"],damage:"1d8",properties:["ammunition","two-handed","loading"]},{id:"shortsword",name:"Shortsword",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["finesse","light"]},{id:"dagger",name:"Dagger",type:"weapon",archetypes:["archer"],rarities:["common","uncommon","elite","boss"],damage:"1d4",properties:["finesse","light","thrown"]},{id:"quarterstaff",name:"Quarterstaff",type:"weapon",archetypes:["support"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile","two-handed"]},{id:"support-mace",name:"Mace",type:"weapon",archetypes:["support"],rarities:["common","uncommon","elite","boss"],damage:"1d6",properties:["versatile"]},{id:"support-dagger",name:"Dagger",type:"weapon",archetypes:["support"],rarities:["common","uncommon"],damage:"1d4",properties:["finesse","light","thrown"]},{id:"leather-armor",name:"Leather Armor",type:"armor",archetypes:["brute","archer","support"],rarities:["common","uncommon","elite","boss"],acBonus:11},{id:"scale-mail",name:"Scale Mail",type:"armor",archetypes:["brute","support"],rarities:["uncommon","elite","boss"],acBonus:14},{id:"chain-mail",name:"Chain Mail",type:"armor",archetypes:["brute","support"],rarities:["elite","boss"],acBonus:16},{id:"plate-armor",name:"Plate Armor",type:"armor",archetypes:["brute"],rarities:["boss"],acBonus:18},{id:"shield",name:"Shield",type:"shield",archetypes:["brute","support"],rarities:["uncommon","elite","boss"],acBonus:2}];function Ra(s,e){return qe.filter(t=>t.archetypes.includes(s)&&t.rarities.includes(e))}function Zr(s,e){return Ra(s,e).filter(t=>t.type==="weapon")}function Xr(s,e){return Ra(s,e).filter(t=>t.type==="armor")}function Ii(s,e){return Ra(s,e).filter(t=>t.type==="shield")}const fe=class fe{static generate(e){const{archetype:t,rarity:a,seed:i,includeShield:n=!0}=e,r=new Se(i),o={};if(o.weapon=fe.selectWeapon(t,a,r),o.armor=fe.selectArmor(t,a,r),n&&fe.canUseShield(t)&&Ii(t,a).length>0){const c=fe.SHIELD_CHANCE[a];r.random()<c&&(o.shield=fe.selectShield(t,a))}return o}static selectWeapon(e,t,a){const i=Zr(e,t);if(i.length===0)return qe.find(o=>o.id==="dagger");const n=fe.WEAPON_PRIORITY[e][t],r=i.filter(o=>n.includes(o.id));return r.length>0&&a.random()<.7?a.randomChoice(r):a.randomChoice(i)}static selectArmor(e,t,a){const i=Xr(e,t);if(i.length===0)return qe.find(o=>o.id==="leather-armor");const n=fe.ARMOR_PRIORITY[e][t],r=i.filter(o=>n.includes(o.id));return r.length>0&&a.random()<.8?a.randomChoice(r):a.randomChoice(i)}static selectShield(e,t){const a=Ii(e,t);return a.length===0?qe.find(i=>i.id==="shield"):a[0]}static canUseShield(e){return e!=="archer"}static getEquipmentName(e){return Yr[e]||e}static getAllTemplates(){return[...qe]}static getTemplateById(e){return qe.find(t=>t.id===e)}};fe.WEAPON_PRIORITY={brute:{common:["handaxe","brute-mace"],uncommon:["longsword","brute-mace"],elite:["greataxe","longsword"],boss:["greataxe","longsword"]},archer:{common:["light-crossbow","dagger"],uncommon:["longbow","shortsword"],elite:["longbow","shortsword"],boss:["longbow","shortsword"]},support:{common:["support-dagger","quarterstaff"],uncommon:["quarterstaff","support-mace"],elite:["quarterstaff","support-mace"],boss:["quarterstaff","support-mace"]}},fe.ARMOR_PRIORITY={brute:{common:["leather-armor"],uncommon:["scale-mail","leather-armor"],elite:["chain-mail","scale-mail"],boss:["plate-armor","chain-mail"]},archer:{common:["leather-armor"],uncommon:["leather-armor"],elite:["leather-armor"],boss:["leather-armor"]},support:{common:["leather-armor"],uncommon:["scale-mail","leather-armor"],elite:["chain-mail","scale-mail"],boss:["chain-mail","scale-mail"]}},fe.SHIELD_CHANCE={common:.25,uncommon:.5,elite:.75,boss:1};let Ge=fe;const vt={0:{},.125:{},.25:{},.5:{},1:{1:3},2:{1:4},3:{1:4,2:2},4:{1:4,2:3},5:{1:4,2:3,3:2},6:{1:4,2:3,3:3},7:{1:4,2:3,3:3},8:{1:4,2:3,3:3,4:1},9:{1:4,2:3,3:3,4:2},10:{1:4,2:3,3:3,4:2}},bt={support:{archetype:"support",cantrips:[{id:"support_sacred_flame",name:"Sacred Flame",level:0,school:"evocation",effect:"A flame of divine brightness springs from your hand. Deals radiant damage to one target.",damage:"1d8",damageType:"radiant",rangeFeet:60,tags:["damage","ranged"]},{id:"support_guidance",name:"Guidance",level:0,school:"divination",effect:"You touch one willing creature. Once before the spell ends, the target can roll a d4 and add the number rolled to one ability check of its choice.",rangeFeet:30,concentration:!1,tags:["buff","utility"]},{id:"support_resistance",name:"Resistance",level:0,school:"abjuration",effect:"You touch one willing creature. The target gains resistance to one damage type of your choice until the spell ends.",rangeFeet:30,concentration:!0,tags:["buff","defense"]}],level1:[{id:"support_bless",name:"Bless",level:1,school:"enchantment",effect:"You bless up to three creatures of your choice within range. Whenever a target makes an attack roll or saving throw before the spell ends, the target can roll a d4 and add the number rolled to the d20.",rangeFeet:30,concentration:!0,tags:["buff","ally","multi-target"]},{id:"support_bane",name:"Bane",level:1,school:"enchantment",effect:"Up to three creatures of your choice that you can see within range must make a Wisdom saving throw. On a failed save, the target has disadvantage on attack rolls for the duration.",save:"WIS",rangeFeet:30,concentration:!0,tags:["debuff","control"]},{id:"support_cure_wounds",name:"Cure Wounds",level:1,school:"evocation",effect:"A creature you touch regains a number of hit points equal to 1d8 + your spellcasting ability modifier.",rangeFeet:30,tags:["healing","ally"]},{id:"support_healing_word",name:"Healing Word",level:1,school:"evocation",effect:"A creature of your choice that you can see within range regains hit points equal to 1d4 + your spellcasting ability modifier.",rangeFeet:60,tags:["healing","ally","bonus-action"]},{id:"support_command",name:"Command",level:1,school:"enchantment",effect:"You speak a one-word command to a creature you can see within range. The target must succeed on a Wisdom save or follow the command on its next turn.",save:"WIS",rangeFeet:60,tags:["control","debuff"]}],level2:[{id:"support_aid",name:"Aid",level:2,school:"evocation",effect:"Your spell bolsters your allies with toughness and resolve. Up to three creatures gain temporary hit points and advantage on certain ability checks.",rangeFeet:30,tags:["buff","ally","multi-target"]},{id:"support_lesser_restoration",name:"Lesser Restoration",level:2,school:"abjuration",effect:"You touch a creature and can end either one disease or one condition afflicting it. The condition can be blinded, deafened, paralyzed, or poisoned.",rangeFeet:30,tags:["healing","ally","utility"]},{id:"support_spiritual_weapon",name:"Spiritual Weapon",level:2,school:"evocation",effect:"You create a spectral weapon within range. It can be used to attack, dealing force damage on a hit.",damage:"1d8 + 1",damageType:"force",rangeFeet:60,concentration:!1,tags:["summon","damage"]},{id:"support_shatter",name:"Shatter",level:2,school:"evocation",effect:"A sudden loud ringing sound, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution saving throw.",damage:"3d8",damageType:"thunder",save:"CON",rangeFeet:60,tags:["damage","aoe"]}],level3:[{id:"support_spirit_guardians",name:"Spirit Guardians",level:3,school:"conjuration",effect:"You call forth spirits to protect you. The spirits are intangible, spectral guardians that flank you. Each creature of your choice that you can see within range must succeed on a Wisdom saving throw or be restrained.",damage:"2d8",damageType:"radiant",save:"WIS",rangeFeet:30,concentration:!0,tags:["defense","control","aoe"]},{id:"support_revivify",name:"Revivify",level:3,school:"conjuration",effect:"You touch a creature that has died within the last minute. That creature returns to life with 1 hit point.",rangeFeet:30,tags:["healing","ally","utility"]},{id:"support_mass_healing_word",name:"Mass Healing Word",level:3,school:"evocation",effect:"Up to six creatures of your choice that you can see within range regain hit points equal to 1d4 + your spellcasting ability modifier.",rangeFeet:60,tags:["healing","ally","multi-target"]}]},archer:{archetype:"archer",cantrips:[{id:"archer_ray_of_frost",name:"Ray of Frost",level:0,school:"evocation",effect:"A frigid beam of blue-white light streaks toward a creature within range. On a hit, the target takes cold damage and its speed is reduced by 10 feet until the start of your next turn.",damage:"1d8",damageType:"cold",rangeFeet:60,tags:["damage","control","ranged"]},{id:"archer_shocking_grasp",name:"Shocking Grasp",level:0,school:"evocation",effect:"Lightning springs from your hand to deliver a shock to a creature you try to touch. The target takes lightning damage and cannot take reactions until its next turn.",damage:"1d8",damageType:"lightning",rangeFeet:30,tags:["damage","melee","control"]},{id:"archer_true_strike",name:"True Strike",level:0,school:"divination",effect:"You extend your hand and point a finger at a target in range. Your magic grants you a brief insight into the target's defenses. On your next turn, you gain advantage on your first attack roll against the target.",rangeFeet:30,tags:["buff","self"]}],level1:[{id:"archer_misty_step",name:"Misty Step",level:1,school:"conjuration",effect:"You briefly vanish from existence and teleport to a nearby space. During your next turn, you gain advantage on the first melee attack you make against an enemy.",rangeFeet:90,tags:["mobility","escape","buff"]},{id:"archer_hold_person",name:"Hold Person",level:1,school:"enchantment",effect:"Choose a humanoid that you can see within range. The target must succeed on a Wisdom saving throw or be paralyzed for the duration.",save:"WIS",rangeFeet:60,concentration:!0,tags:["control","debuff"]},{id:"archer_ray_of_sickness",name:"Ray of Sickness",level:1,school:"necromancy",effect:"A ray of sickening greenish energy lashes out toward a creature within range. The target must make a Constitution saving throw or take poison damage and have disadvantage on its next attack.",damage:"2d8",damageType:"poison",save:"CON",rangeFeet:60,tags:["damage","debuff"]},{id:"archer_thunderwave",name:"Thunderwave",level:1,school:"evocation",effect:"A wave of thunderous force sweeps out from you. Each creature in a 15-foot cube must make a Constitution saving throw or take thunder damage and be pushed 10 feet away.",damage:"2d8",damageType:"thunder",save:"CON",rangeFeet:30,tags:["damage","aoe","control"]}],level2:[{id:"archer_web",name:"Web",level:2,school:"conjuration",effect:"You cast a web of thick, sticky strands at a point within range. Creatures in the area must make a Dexterity saving throw or be restrained.",save:"DEX",rangeFeet:60,concentration:!1,tags:["control","aoe","restrain"]},{id:"archer_invisibility",name:"Invisibility",level:2,school:"illusion",effect:"A creature you touch becomes invisible until the spell ends. Anything the target is wearing or carrying is invisible as long as it is on the target's person.",rangeFeet:30,concentration:!0,tags:["stealth","defense","utility"]},{id:"archer_melfs_acid_arrow",name:"Melf's Acid Arrow",level:2,school:"evocation",effect:"A shimmering green arrow streaks toward a target within range and bursts in a spray of acid. The target takes acid damage immediately and additional acid damage at the end of its next turn.",damage:"4d4",damageType:"acid",rangeFeet:90,tags:["damage","ranged","dot"]},{id:"archer_scorching_ray",name:"Scorching Ray",level:2,school:"evocation",effect:"You focus elemental energy into a ray that deals fire damage to a target. The target ignites if it's wearing flammable gear.",damage:"4d6",damageType:"fire",rangeFeet:60,tags:["damage","ranged"]}],level3:[{id:"archer_fly",name:"Fly",level:3,school:"transmutation",effect:"You touch a willing creature. The target gains a flying speed of 60 feet for the duration. When the spell ends, the target falls if it is still aloft.",rangeFeet:30,concentration:!0,tags:["mobility","buff","ally"]},{id:"archer_lightning_bolt",name:"Lightning Bolt",level:3,school:"evocation",effect:"A stroke of lightning forming a line 100 feet long and 5 feet wide bursts from you in a direction you choose. Each creature in the line must make a Dexterity saving throw.",damage:"8d6",damageType:"lightning",save:"DEX",rangeFeet:90,tags:["damage","aoe"]},{id:"archer_gaseous_form",name:"Gaseous Form",level:3,school:"transmutation",effect:"You transform a willing creature you touch into a misty cloud for the duration. The creature can't talk or manipulate objects, and is immune to non-magical damage.",rangeFeet:30,concentration:!1,tags:["defense","utility","escape"]}]},brute:{archetype:"brute",cantrips:[{id:"brute_fire_bolt",name:"Fire Bolt",level:0,school:"evocation",effect:"You hurl a mote of fire at a creature or object within range. On a hit, the target takes fire damage.",damage:"1d10",damageType:"fire",rangeFeet:60,tags:["damage","ranged"]},{id:"brute_shillelagh",name:"Shillelagh",level:0,school:"transmutation",effect:"The wood of a club or quarterstaff you are holding becomes imbued with magic. For the duration, your weapon deals magical damage and has its damage die increased.",damage:"1d8",damageType:"force",rangeFeet:30,concentration:!0,tags:["buff","self","melee"]},{id:"brute_thorn_whip",name:"Thorn Whip",level:0,school:"conjuration",effect:"You create a long, vine-like whip covered in thorns that lashes out at your command toward a creature you can see.",damage:"1d6",damageType:"piercing",rangeFeet:30,tags:["damage","ranged","control"]}],level1:[{id:"brute_burning_hands",name:"Burning Hands",level:1,school:"evocation",effect:"As you hold your hands with thumbs touching and fingers spread, a thin sheet of flames shoots forth from your outstretched fingertips. Each creature in a 15-foot cone must make a Dexterity saving throw.",damage:"3d6",damageType:"fire",save:"DEX",rangeFeet:30,tags:["damage","aoe"]},{id:"brute_divine_favor",name:"Divine Favor",level:1,school:"evocation",effect:"Your prayer empowers you with divine energy. You gain advantage on the first attack roll you make during the spell's duration.",rangeFeet:30,tags:["buff","self"]},{id:"brute_magic_stone",name:"Magic Stone",level:1,school:"transmutation",effect:"You touch one to three pebbles and imbue them with magic. You or another creature can make a ranged spell attack with one of the stones, dealing bludgeoning damage.",damage:"1d6 + 1",damageType:"bludgeoning",rangeFeet:30,tags:["damage","ranged"]},{id:"brute_zephyr_strike",name:"Zephyr Strike",level:1,school:"evocation",effect:"You create a gust of wind that pushes a creature away from you. The target must succeed on a Strength saving throw or be pushed 15 feet away.",save:"STR",rangeFeet:30,tags:["control","push"]}],level2:[{id:"brute_shatter",name:"Shatter",level:2,school:"evocation",effect:"A sudden loud ringing sound, painfully intense, erupts from a point of your choice within range. Each creature in a 10-foot-radius sphere must make a Constitution saving throw.",damage:"3d8",damageType:"thunder",save:"CON",rangeFeet:60,tags:["damage","aoe"]},{id:"brute_branding_smite",name:"Branding Smite",level:2,school:"evocation",effect:"The next time you hit a creature with a melee weapon attack during this spell's duration, your weapon flares with bright light and deals an extra 2d6 radiant damage.",damage:"2d6",damageType:"radiant",rangeFeet:30,tags:["damage","melee","buff"]},{id:"brute_spiritual_weapon",name:"Spiritual Weapon",level:2,school:"evocation",effect:"You create a spectral weapon within range. It can be used to attack, dealing force damage on a hit.",damage:"1d8 + 1",damageType:"force",rangeFeet:60,concentration:!1,tags:["summon","damage"]},{id:"brute_flame_blade",name:"Flame Blade",level:2,school:"evocation",effect:"You evoke a fiery blade in your free hand. The blade is similar in size and shape to a scimitar and sheds bright light in a 10-foot radius.",damage:"3d6",damageType:"fire",rangeFeet:30,concentration:!0,tags:["damage","melee"]}],level3:[{id:"brute_call_lightning",name:"Call Lightning",level:3,school:"conjuration",effect:"A storm cloud appears in the shape of a cylinder that is 10 feet tall with a 10-foot radius and centered on a point you can see within range.",damage:"5d6",damageType:"lightning",save:"DEX",rangeFeet:90,concentration:!0,tags:["damage","aoe","control"]},{id:"brute_elemental_weapon",name:"Elemental Weapon",level:3,school:"transmutation",effect:"A nonmagical weapon you touch becomes a magic weapon. Choose one of the following damage types: acid, cold, fire, lightning, or thunder.",rangeFeet:30,tags:["buff","melee","utility"]},{id:"brute_blur",name:"Blur",level:3,school:"illusion",effect:"Your body becomes blurred, shifting and wavering to all who can see you. Attacks against you have disadvantage unless the attacker can't see.",rangeFeet:30,concentration:!0,tags:["defense","debuff"]}]}},Ei={common:{cantrips:1,spells:1},uncommon:{cantrips:2,spells:2},elite:{cantrips:2,spells:3},boss:{cantrips:3,spells:4}},Mi={common:1,uncommon:2,elite:3,boss:4};class Ee{static generateSpellList(e){const{archetype:t,rarity:a,cr:i,seed:n}=e,r=new Se(n),o=bt[t],l=Ei[a],c=Mi[a],u=[],d=[...o.cantrips];for(let f=0;f<l.cantrips&&d.length>0;f++){const v=r.randomInt(0,d.length),w=d[v];u.push(w),d.splice(v,1)}const h=[],p=[1,2,3,4];for(const f of p){if(f>c)break;const v=o[`level${f}`];if(!v||v.length===0)continue;const w=vt[i]?.[f];if(!w||w===0)continue;const E=[...v];for(let I=0;I<w&&E.length>0;I++){const D=r.randomInt(0,E.length),x=E[D];h.push(x),E.splice(D,1)}}const g=l.spells-h.length;if(g>0){const f=[];for(const w of p){if(w>c)break;const E=o[`level${w}`];E&&f.push(...E)}const v=[...f];for(let w=0;w<g&&v.length>0;w++){const E=r.randomInt(0,v.length),I=v[E];h.some(D=>D.id===I.id)||h.push(I),v.splice(E,1)}}const m=Ee.getSpellSlotsForCR(i);return{cantrips:u,spells:h,slots:m}}static generateSpellListWithRNG(e){const{archetype:t,rarity:a,cr:i,rng:n}=e,r=bt[t],o=Ei[a],l=Mi[a],c=[],u=[...r.cantrips];for(let m=0;m<o.cantrips&&u.length>0;m++){const f=n.randomInt(0,u.length),v=u[f];c.push(v),u.splice(f,1)}const d=[],h=[1,2,3,4];for(const m of h){if(m>l)break;const f=r[`level${m}`];if(!f||f.length===0)continue;const v=vt[i]?.[m];if(!v||v===0)continue;const w=[...f];for(let E=0;E<v&&w.length>0;E++){const I=n.randomInt(0,w.length),D=w[I];d.push(D),w.splice(I,1)}}const p=o.spells-d.length;if(p>0){const m=[];for(const v of h){if(v>l)break;const w=r[`level${v}`];w&&m.push(...w)}const f=[...m];for(let v=0;v<p&&f.length>0;v++){const w=n.randomInt(0,f.length),E=f[w];d.some(I=>I.id===E.id)||d.push(E),f.splice(w,1)}}const g=Ee.getSpellSlotsForCR(i);return{cantrips:c,spells:d,slots:g}}static getSpellSlotsForCR(e){const t=Object.keys(vt).map(Number).sort((i,n)=>i-n);let a=0;for(const i of t)if(e>=i)a=i;else break;return vt[a]||{}}static archetypeCanCast(e){return Object.prototype.hasOwnProperty.call(bt,e)}static getSpellListForArchetype(e){return bt[e]}static spellToFeature(e){return{id:e.id,name:e.name,description:e.effect,type:e.level===0?"cantrip":"active",class:"Enemy",level:e.level,source:"spellcasting",tags:e.tags||[],isSpell:!0,school:e.school,damage:e.damage,damageType:e.damageType,save:e.save,range:e.rangeFeet,concentration:e.concentration}}static spellsToFeatures(e){const t=[];for(const a of e.cantrips)t.push(Ee.spellToFeature(a));for(const a of e.spells)t.push(Ee.spellToFeature(a));return t}static shouldHaveSpellcasting(e,t){return e==="support"||t==="elite"||t==="boss"}}const wt={brute:[{id:"tail_attack",name:"Tail Attack",description:"The boss makes a powerful tail strike against one creature within reach.",cost:1,effect:"Melee attack with bonus damage",damage:"2d8 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["melee","damage"]},{id:"devour",name:"Devour",description:"The boss devours a nearby creature, healing itself and dealing massive damage.",cost:3,effect:"Massive damage + self heal",damage:"4d12 + 5",damageType:"necrotic",archetypes:["brute"],tags:["melee","damage","heal"]},{id:"trample",name:"Trample",description:"The boss charges forward, crushing creatures in its path.",cost:2,effect:"AoE damage + prone",damage:"3d10 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["aoe","damage","control"]},{id:"earthquake_slam",name:"Earthquake Slam",description:"The boss strikes the ground, creating a shockwave that knocks nearby creatures prone.",cost:2,effect:"AoE damage + prone",damage:"2d10 + 5",damageType:"bludgeoning",archetypes:["brute"],tags:["aoe","damage","control"]},{id:"rage_smash",name:"Rage Smash",description:"The boss enters a rage and delivers a crushing blow with advantage.",cost:1,effect:"Single target attack with advantage",damage:"2d10 + 7",damageType:"bludgeoning",archetypes:["brute"],tags:["melee","damage","buff"]},{id:"charge",name:"Charge",description:"The boss moves up to its speed and makes a powerful attack.",cost:1,effect:"Movement + melee attack with bonus damage",damage:"2d8 + 5",damageType:"piercing",archetypes:["brute"],tags:["movement","melee","damage"]}],archer:[{id:"snipe",name:"Snipe",description:"The boss makes a precise ranged attack against one creature it can see.",cost:1,effect:"Ranged attack with advantage",damage:"2d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","damage"]},{id:"volley_shot",name:"Volley Shot",description:"The boss looses multiple arrows at different targets.",cost:2,effect:"Multiple ranged attacks (up to 3 targets)",damage:"1d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","aoe","damage"]},{id:"disabling_shot",name:"Disabling Shot",description:"The boss fires a shot aimed to disable a creature's movement.",cost:1,effect:"Ranged attack + restrained condition",damage:"1d8 + 4",damageType:"piercing",archetypes:["archer"],tags:["ranged","control"]},{id:"evasive_maneuver",name:"Evasive Maneuver",description:"The boss dashes away without provoking opportunity attacks.",cost:1,effect:"Disengage + movement bonus",archetypes:["archer"],tags:["movement","defense"]},{id:"shadow_step",name:"Shadow Step",description:"The boss teleports to a nearby location and can make an attack.",cost:2,effect:"Teleport + optional ranged attack",damage:"2d6 + 4",damageType:"necrotic",archetypes:["archer"],tags:["movement","damage"]},{id:"multi_shot",name:"Multi-Shot",description:"The boss fires a barrage of arrows in a cone.",cost:3,effect:"Cone AoE damage",damage:"3d6 + 4",damageType:"piercing",archetypes:["archer"],tags:["aoe","damage"]}],support:[{id:"rally",name:"Rally",description:"The boss lets out a commanding cry, granting an ally an immediate attack.",cost:1,effect:"Adjacent ally makes immediate attack",archetypes:["support"],tags:["ally","buff"]},{id:"frightful_presence",name:"Frightful Presence",description:"The boss exudes an aura of terror, forcing creatures to make a fear save.",cost:1,effect:"AoE fear save or frightened",archetypes:["support"],tags:["aoe","control","fear"]},{id:"healing_aura",name:"Healing Aura",description:"The boss channels power, healing itself and nearby allies.",cost:2,effect:"Heal self + adjacent allies (3d6 + CHA mod)",archetypes:["support"],tags:["heal","ally","aoe"]},{id:"dispel_magic",name:"Dispel Magic",description:"The boss attempts to end a spell affecting it or an ally.",cost:1,effect:"Counter one spell effect",archetypes:["support"],tags:["utility","defense"]},{id:"command_ally",name:"Command Ally",description:"The boss telepathically commands an ally to take an action.",cost:2,effect:"Ally takes immediate action",archetypes:["support"],tags:["ally","control"]},{id:"shield_bash",name:"Shield Bash",description:"The boss strikes with its shield, dealing damage and potentially stunning.",cost:1,effect:"Melee damage + potential stun",damage:"2d6 + 3",damageType:"bludgeoning",archetypes:["support"],tags:["melee","damage","control"]},{id:"tactical_reposition",name:"Tactical Reposition",description:"The boss moves strategically to a better position without provoking opportunity attacks.",cost:1,effect:"Movement + disengage",archetypes:["support"],tags:["movement","defense"]}]},St=[{id:"teleport",name:"Teleport",description:"The boss instantly teleports to a nearby location without provoking opportunity attacks.",cost:2,effect:"Instant movement without opportunity attacks",archetypes:["brute","archer","support"],tags:["movement","defense"]},{id:"detect",name:"Detect",description:"The boss attunes its senses, detecting invisible or hidden creatures.",cost:1,effect:"Reveal invisible/hidden creatures nearby",archetypes:["brute","archer","support"],tags:["utility","information"]}],Di={1:3,2:3,3:3,4:3,5:3,6:3,7:3,8:3,9:3,10:3,11:4,12:4,13:4,14:4,15:4,16:5,17:5,18:5,19:5,20:5,21:6},Kr=3;class Te{static generate(e){const{archetype:t,cr:a,seed:i}=e,n=Te.getResistancesForCR(a),r=new Se(i),l=[...wt[t]||[],...St],c=Te.selectLegendaryActions(l,r),u=r.random()<.2?Te.generateLairActionHint(t,r):void 0;return{resistances:n,actions:c,lairActionHint:u}}static generateWithRNG(e){const{archetype:t,cr:a,rng:i}=e,n=Te.getResistancesForCR(a),o=[...wt[t]||[],...St],l=Te.selectLegendaryActions(o,i),c=i.random()<.2?Te.generateLairActionHint(t,i):void 0;return{resistances:n,actions:l,lairActionHint:c}}static selectLegendaryActions(e,t){const a=e.filter(c=>c.tags?.includes("movement"));e.filter(c=>!c.tags?.includes("movement"));const i=[],n=new Set;if(a.length>0){const c=a.filter(u=>!n.has(u.id));if(c.length>0){const u=t.randomChoice(c);i.push(u),n.add(u.id)}}const r=Kr-i.length,o=e.filter(c=>!n.has(c.id)),l=t.shuffle(o);for(let c=0;c<r&&c<l.length;c++)i.push(l[c]);return i}static getResistancesForCR(e){const t=Object.keys(Di).map(Number).sort((a,i)=>i-a);for(const a of t)if(e>=a)return Di[a];return 3}static generateLairActionHint(e,t){const a=["Cave-in risk: Sections of ceiling may collapse on initiative count 20","Lair tremors: DEX save or fall prone at start of each round","Crushing walls: Walls slowly close in, dealing damage each round"],o={brute:a,archer:["Covering fire: Minions provide ranged support from elevated positions","Trapped corridors: DEX save or trigger arrow traps/magical wards","Shadow passages: Boss can teleport between pre-designated shadow points"],support:["Aura of command: Allies gain advantage on attacks within 30ft of boss","Reinforcement calls: Minions arrive in waves on rounds 3 and 5","Lair wards: Magical barriers provide boss temporary protection"]}[e]||a;return t.randomChoice(o)}static getActionById(e){for(const t of Object.values(wt)){const a=t.find(i=>i.id===e);if(a)return a}return St.find(t=>t.id===e)}static getActionsForArchetype(e){return[...wt[e]||[],...St]}static shouldHaveLegendary(e){return e==="boss"}}const Jr={baseMultiplier:1,levelOffset:0,customCurve:new Map,minLevel:0,maxLevel:1/0};function Qr(s,e=Jr){if(e.customCurve.has(s))return e.customCurve.get(s);let t=s*e.baseMultiplier+e.levelOffset;return t=Math.max(e.minLevel,Math.min(e.maxLevel,t)),t}const eo={common:.9,uncommon:.95,elite:1,boss:1.05},to=.2,ao=[{minLevel:1,die:"1d6"},{minLevel:3,die:"1d8"},{minLevel:6,die:"1d10"},{minLevel:10,die:"1d12"},{minLevel:15,die:"2d6"},{minLevel:20,die:"2d8"},{minLevel:25,die:"2d10"},{minLevel:30,die:"3d8"}],Ls=.2,io=.2;function Os(s){switch(s){case"brute":return"STR";case"archer":return"DEX";case"support":return"CHA"}}function Ri(s,e,t){const a=Oe(t),i=eo[t],n=Math.pow(e,i),r=Bs(e);return Math.round(s*n*a.hpMultiplier*r)}function Bs(s){return s>=1?1:s<=0?.5:.5+.5*s}function _t(s,e,t,a){const i=Os(a),n=Math.floor(((s[i]??10)-10)/2),r=Math.ceil(1+(e-1)/4),o=so(e),l=Math.floor(Ls*(e-1)),c=n+l,u=Math.floor(to*(e-1)),d=r+n+u;return{damageDie:o,damageModifier:c,attackBonus:d}}function so(s){let e="d6";for(const t of ao)s>=t.minLevel&&(e=t.die);return e}function Pi(s,e,t,a){const i=Math.floor((s.DEX-10)/2);let n=t;a.armor?.acBonus&&(n=a.armor.acBonus);let r=0;a.shield?.acBonus&&(r=a.shield.acBonus);const o=Math.floor(io*(e-1));return n+i+r+o}function no(s,e,t){const a=Os(t),i=Math.floor(((s[a]??10)-10)/2),n=Math.floor(Ls*(e-1));return i+n}const C=class C{static getSeededRNG(e,t){const a=t!==void 0?`${e}-${t}`:e;return new Se(a)}static applyAudioStatInfluence(e,t){const{bass_dominance:a=0,mid_dominance:i=0,treble_dominance:n=0}=t;let r=0,o=0,l=0,c=0,u=0;return a>.6?(r=1,l=1):a>.4&&(r=1),n>.6&&(o=1),i>.6?(c=1,u=1):i>.4&&(c=1),a<.5&&i<.5&&n<.5&&Math.abs(a-i)<.2&&Math.abs(i-n)<.2&&(r=1,o=1,l=1,c=1,u=1),{STR:Math.min(e.STR+r,e.STR+C.MAX_AUDIO_INFLUENCE),DEX:Math.min(e.DEX+o,e.DEX+C.MAX_AUDIO_INFLUENCE),CON:Math.min(e.CON+l,e.CON+C.MAX_AUDIO_INFLUENCE),INT:e.INT,WIS:Math.min(e.WIS+c,e.WIS+C.MAX_AUDIO_INFLUENCE),CHA:Math.min(e.CHA+u,e.CHA+C.MAX_AUDIO_INFLUENCE)}}static scaleStatsForRarity(e,t,a){let n=Oe(t).statMultiplier;return a!==void 0&&(n=C.getStatMultiplierForFractionalCR(a)*n),{STR:Math.round(e.STR*n),DEX:Math.round(e.DEX*n),CON:Math.round(e.CON*n),INT:Math.round(e.INT*n),WIS:Math.round(e.WIS*n),CHA:Math.round(e.CHA*n)}}static getTemplateById(e){return Xe.find(t=>t.id===e)}static selectTemplate(e,t,a,i){let n=[...Xe];if(t&&(n=n.filter(r=>r.category===t)),a&&(n=n.filter(r=>r.archetype===a)),n.length===0)throw new Error(`No enemy templates match filters: category=${t}, archetype=${a}`);return i?C.weightedSelectionByAudio(n,i,e):e.randomChoice(n)}static weightedSelectionByAudio(e,t,a){const i=e.map(n=>{const{audioPreference:r}=n,o=(t.bass_dominance||0)*r.bass+(t.mid_dominance||0)*r.mid+(t.treble_dominance||0)*r.treble;return[n,Math.max(.1,o)]});return a.weightedChoice(i)}static scaleSignatureAbility(e,t,a,i,n,r){const o=Oe(t),l=r?.attackLevel!==void 0?_t(a,r.attackLevel,t,n).damageDie:`1d${o.signatureDieSize}`,c=r?.attackLevel!==void 0?_t(a,r.attackLevel,t,n).damageModifier:no(a,i,n);return{id:e.id,name:e.name,description:e.description,type:"active",class:"Enemy",level:1,source:"default",tags:[e.attackType,e.damageType],effects:[],attack:{name:e.name,damage:`${l} + ${c}`,damage_dice:l,damage_type:e.damageType,type:e.attackType,range:e.range,properties:e.properties}}}static generateBossName(e,t){const a=t.random();if(a<.5){const r=t.randomChoice(C.BOSS_TITLES);return`${e}, ${r}`}if(a<.8)return`${t.randomChoice(C.BOSS_PREFIXES)} ${e}`;const i=["the Void","Ruin","Pain","Death","the Abyss","the Forgotten Lands","Eternal Night","Chaos","Doom","the Broken World","Shadows","the Endless Void"],n=t.randomChoice(i);return`${e} of ${n}`}static generateBossFeatures(e,t,a){const i=[],n={...t};if(n.attack&&typeof n.attack=="object"){const o=n.attack;if(o.damage_dice){const l=o.damage_dice.match(/^d(\d+)$/);if(l){const c=parseInt(l[1],10);o.damage_dice=`2d${c}`;const u=o.damage?.match(/^(\d+d\d+)(\s*\+\s*\d+)?$/);if(u){const d=u[1],h=u[2]||"";o.damage=`${d.replace(/^d\d+$/,`2d${c}`)}${h}`}}}}i.push(n);const r=C.generateUltimateAbility(e.archetype,a);return i.push(r),i}static generateUltimateAbility(e,t){const a=[{id:"cataclysmic_smash",name:"Cataclysmic Smash",description:"The boss unleashes all its power in a devastating blow that cracks the earth itself.",damage:"6d12 + 10",damageType:"bludgeoning"},{id:"unbreaking_frenzy",name:"Unbreaking Frenzy",description:"The boss enters a state of pure rage, attacking everything nearby with impossible speed.",damage:"4d10 + 8",damageType:"slashing"},{id:"world_ender",name:"World Ender",description:"A single attack charged with all the boss's strength, capable of felling the mightiest foes.",damage:"8d8 + 12",damageType:"bludgeoning"}],i=[{id:"phantom_ barrage",name:"Phantom Barrage",description:"The boss fires ethereal arrows that phase through cover, striking all enemies in a massive cone.",damage:"5d8 + 8",damageType:"piercing"},{id:"death_from_above",name:"Death from Above",description:"The boss leaps impossibly high, then rains destruction in a devastating area attack.",damage:"4d10 + 10",damageType:"piercing"},{id:"final_Stand",name:"Final Stand",description:"With uncanny precision, the boss fires a single arrow that cannot miss, dealing massive damage.",damage:"10d6 + 15",damageType:"piercing"}],n=[{id:"dark_miracle",name:"Dark Miracle",description:"The boss performs a ritual of incredible power, completely healing itself and all allies.",effect:"Heal self and all allies for 50% max HP"},{id:"soul_anchor",name:"Soul Anchor",description:"The boss binds the souls of all creatures nearby, preventing all movement and actions for a duration.",effect:"AoE stun/paralyze all creatures, no save"},{id:"realm_of_despair",name:"Realm of Despair",description:"The boss projects an aura of pure hopelessness, weakening all enemies' attacks and defenses.",effect:"All enemies have disadvantage on attacks and -2 AC"}];let r,o="bludgeoning";switch(e){case"brute":r=t.randomChoice(a),o="bludgeoning";break;case"archer":r=t.randomChoice(i),o="piercing";break;case"support":r=t.randomChoice(n);break;default:r=t.randomChoice(a),o="bludgeoning"}const l={id:r.id,name:r.name,description:r.description,type:"ultimate",class:"Enemy",level:1,source:"boss",tags:["ultimate","once_per_encounter",e],effects:[],uses_per_encounter:1,max_uses_per_encounter:1};if("damage"in r){const c=r;l.attack={name:c.name,damage:c.damage,damage_dice:c.damage,damage_type:o,type:e==="archer"?"ranged":"melee",properties:["ultimate","once_per_encounter"]}}else{const c=r;l.effect=c.effect}return l}static generateAbilities(e,t,a,i,n,r,o){const c=[n&&r?C.scaleSignatureAbility(e.signatureAbility,t,n,r,e.archetype,o):C.scaleSignatureAbility(e.signatureAbility,t,e.baseStats,1,e.archetype,o)];let u;if(Ee.shouldHaveSpellcasting(e.archetype,t)){const p=i||C.getCRForRarity(t);u=Ee.generateSpellListWithRNG({archetype:e.archetype,rarity:t,cr:p,rng:a});const g=Ee.spellsToFeatures(u);c.push(...g)}const h=Oe(t).extraAbilityCount;if(h>0){const p=C.selectExtraAbilities(e.archetype,h,a);c.push(...p)}return{abilities:c,spellConfig:u}}static selectExtraAbilities(e,t,a){const i=C.ARCHETYPE_TAGS[e],r=re.getInstance().getAllClassFeatures(),o=[];for(const d of r.values())o.push(...d);const l=o.filter(d=>!d.tags||d.tags.length===0?!1:d.tags.some(h=>i.some(p=>h.toLowerCase().includes(p.toLowerCase())||p.toLowerCase().includes(h.toLowerCase()))));if(l.length===0)return[];const c=[],u=[...l];for(let d=0;d<t&&u.length>0;d++){const h=a.randomInt(0,u.length);c.push(u[h]),u.splice(h,1)}return c.map(d=>({id:d.id,name:d.name,description:d.description,type:d.type,class:"Enemy",level:1,source:d.source,tags:d.tags||[],effects:d.effects||[]}))}static generate(e){const{seed:t,cr:a,level:i,templateId:n,rarity:r="common",difficultyMultiplier:o=1,audioProfile:l,track:c,statLevels:u}=e;if(l&&!c)throw new Error("track is required when audioProfile is provided");const d=C.getSeededRNG(t);let h;if(n){const P=C.getTemplateById(n);if(!P)throw new Error(`Unknown enemy template ID: ${n}`);h=P}else{const P=e.category,L=e.archetype;h=C.selectTemplate(d,P,L,l)}const p=a??C.getCRForRarity(r),g=i??C.getLevelFromCR(p);let m=C.scaleStatsForRarity(h.baseStats,r,a);l&&(m=C.applyAudioStatInfluence(m,l));const f={STR:Math.floor((m.STR-10)/2),DEX:Math.floor((m.DEX-10)/2),CON:Math.floor((m.CON-10)/2),INT:Math.floor((m.INT-10)/2),WIS:Math.floor((m.WIS-10)/2),CHA:Math.floor((m.CHA-10)/2)};Oe(r);let v;u?.hpLevel!==void 0?v=Ri(h.baseHP,u.hpLevel,r):v=Ri(h.baseHP,g,r),o!==1&&(v=Math.round(v*o));const w=Math.ceil(1+(g-1)/4);Ee.shouldHaveSpellcasting(h.archetype,r);const{abilities:E,spellConfig:I}=C.generateAbilities(h,r,d,p,m,g,u);let D=E;if(r==="boss"){const P=D[0];D=C.generateBossFeatures(h,P,d)}let x=[],N;if(Te.shouldHaveLegendary(r)){const P=C.getSeededRNG(`${t}-legendary`);N=Te.generateWithRNG({archetype:h.archetype,cr:p,rng:P}),x=N.actions.map(L=>({id:L.id,name:L.name,description:L.description,type:"active",class:"Enemy",level:1,source:"legendary",tags:L.tags||["legendary"],effects:[],legendary_cost:L.cost,legendary_effect:L.effect,legendary_damage:L.damage,legendary_damage_type:L.damageType})),D.push(...x)}const $=Ge.generate({archetype:h.archetype,rarity:r,seed:`${t}-equipment`});let H,te;if(u?.attackLevel!==void 0){const P=_t(m,u.attackLevel,r,h.archetype);H=P.damageDie,te=P.damageModifier}else{const P=_t(m,g,r,h.archetype);H=P.damageDie,te=P.damageModifier}const Q=[];if($.weapon){const P=Ge.getEquipmentName($.weapon.id),L=Ze[P];L&&Q.push({name:P,damage:`${H} + ${te}`,damage_dice:H,damage_type:L.damage?.damageType||h.signatureAbility.damageType,type:h.signatureAbility.attackType,range:h.signatureAbility.range,properties:$.weapon.properties||[],equipped:!0})}Q.length===0&&Q.push({name:h.signatureAbility.name,damage:`${H} + ${te}`,damage_dice:H,damage_type:h.signatureAbility.damageType,type:h.signatureAbility.attackType,range:h.signatureAbility.range,properties:h.signatureAbility.properties||[],equipped:!0});const X=[];let de=0;if($.armor){const P=Ge.getEquipmentName($.armor.id);X.push(P),$.armor.acBonus&&(de=$.armor.acBonus-h.baseAC)}if($.shield){const P=Ge.getEquipmentName($.shield.id);X.push(P),$.shield.acBonus&&(de+=$.shield.acBonus)}let ee;if(u?.defenseLevel!==void 0?ee=Pi(m,u.defenseLevel,h.baseAC,$):ee=Pi(m,g,h.baseAC,$),a!==void 0){const P=h.archetype==="brute"?"STR":h.archetype==="archer"?"DEX":"CHA",L=C.estimateWeaponDPR(H,te,w,f[P]),_e=C.getMinHPRatio(a),ge=Math.ceil(L*_e);v<ge&&(v=ge)}return{name:r==="boss"?C.generateBossName(h.name,d):h.name,race:"Enemy",class:"Monster",subrace:r,level:g,ability_scores:m,ability_modifiers:f,proficiency_bonus:w,hp:{current:v,max:v,temp:0},armor_class:ee,initiative:f.DEX,speed:h.baseSpeed,skills:{},saving_throws:{STR:!0,DEX:!0,CON:!0,INT:!1,WIS:!1,CHA:!1},racial_traits:[],class_features:D.map(P=>P.id||P.name||`${P}`),equipment:{weapons:Q,armor:X,items:[],totalWeight:0,equippedWeight:0},spells:I?{spell_slots:Object.fromEntries(Object.entries(I.slots).map(([P,L])=>[Number(P),{total:L,used:0}])),known_spells:I.spells.map(P=>P.name),cantrips:I.cantrips.map(P=>P.name)}:{spell_slots:{},known_spells:[],cantrips:[]},...I?{combat_spells:[...I.cantrips,...I.spells]}:{},xp:{current:0,next_level:0},seed:t,generated_at:new Date().toISOString(),...p!==void 0?{cr:p}:{},...u?{stat_levels:u}:{},...N?{legendary_config:{resistances_per_day:N.resistances,actions:N.actions.map(P=>({id:P.id,name:P.name,cost:P.cost,effect:P.effect,damage:P.damage,damage_type:P.damageType,description:P.description,tags:P.tags})),lair_action_hint:N.lairActionHint}}:{}}}static getLevelFromCR(e){return Qr(e)}static getStatMultiplierForFractionalCR(e){return Bs(e)}static getDamageDieForRarity(e){return`1d${Oe(e).signatureDieSize}`}static getCRForRarity(e){return{common:.25,uncommon:.5,elite:1,boss:2}[e]||.25}static estimateWeaponDPR(e,t,a,i){const n=e.match(/(\d+)d(\d+)/);if(!n)return 0;const r=parseInt(n[1]),o=parseInt(n[2]),l=r*(o+1)/2+t,c=a+i,u=Math.max(.05,(10+c)/20);return l*u}static getMinHPRatio(e){return 3+(e-1)*(3/29)}static generateEncounter(e,t){const{seed:a,count:i,difficulty:n="medium",baseRarity:r="common",scaleRarityWithCR:o=!1,difficultyMultiplier:l=1,category:c,archetype:u,templateId:d,enemyMix:h="uniform",templates:p,audioProfile:g,track:m,enableLeaderPromotion:f=!0}=t;if(g&&!m)throw new Error("track is required when audioProfile is provided");if(i<=0)return[];if(r==="boss"&&i>1)throw new Error(`Boss encounters must have count=1. Requested count=${i} with baseRarity='boss'. Boss enemies are designed for solo encounters against a party. Use a lower rarity (common/uncommon/elite) for multi-enemy encounters, or set count=1 for a boss encounter.`);const v=e.map(X=>X.level),w=Wt(v,n),E=Math.round(w*l),I=ot(i),D=Math.round(E/I/i);let x=Ta(D);const N=C.getSeededRNG(a),$=N.random();$<.25?x=Math.min(x*1.5,30):$>.75&&(x=Math.max(x*.67,.125));const H=o?C.getRarityDistribution(i,x):Array(i).fill(r),te=C.selectTemplatesForMix(i,N,h,p,c,u,g),Q=[];for(let X=0;X<i;X++){const de=te[X];if(!de)throw new Error(`No template available for enemy at index ${X}`);const ee=C.generate({seed:`${a}-${X}`,templateId:de.id,cr:x,rarity:H[X],difficultyMultiplier:l,audioProfile:g,track:m});Q.push(ee)}return f&&!o&&C.applyLeaderPromotion(Q,N,r),Q}static generateEncounterByCR(e){const{seed:t,count:a=1,targetCR:i=1,baseRarity:n="common",scaleRarityWithCR:r=!1,difficultyMultiplier:o=1,category:l,archetype:c,templateId:u,enemyMix:d="uniform",templates:h,audioProfile:p,track:g,enableLeaderPromotion:m=!0,statLevels:f}=e;if(p&&!g)throw new Error("track is required when audioProfile is provided");if(a<=0)return[];if(i<0)throw new Error(`Invalid targetCR: ${i}. Must be >= 0`);if(n==="boss"&&a>1)throw new Error(`Boss encounters must have count=1. Requested count=${a} with baseRarity='boss'. Boss enemies are designed for solo encounters against a party. Use a lower rarity (common/uncommon/elite) for multi-enemy encounters, or set count=1 for a boss encounter.`);const v=ot(a);let w=i;v>1&&a>1&&(w=Math.max(i/Math.sqrt(v),.125));const E=r?C.getRarityDistribution(a,i):Array(a).fill(n),I=C.getSeededRNG(t),D=C.selectTemplatesForMix(a,I,d,h,l,c,p),x=[];for(let N=0;N<a;N++){const $=D[N];if(!$)throw new Error(`No template available for enemy at index ${N}`);const H=C.generate({seed:`${t}-${N}`,templateId:$.id,cr:w,rarity:E[N],difficultyMultiplier:o,audioProfile:p,track:g,...f?{statLevels:f}:{}});x.push(H)}return m&&!r&&C.applyLeaderPromotion(x,I,n),x}static selectTemplatesForMix(e,t,a,i,n,r,o){const l=[];if(a==="custom"&&i)for(let c=0;c<e;c++){const u=i[c%i.length],d=C.getTemplateById(u);if(!d)throw new Error(`Unknown template ID in custom mix: ${u}`);l.push(d)}else if(a==="category"){if(!n)throw new Error('category option is required when using enemyMix: "category"');for(let c=0;c<e;c++){const u=C.selectTemplate(t,n,r,o);l.push(u)}}else if(a==="random")for(let c=0;c<e;c++){const u=C.selectTemplate(t,void 0,r,o);l.push(u)}else{const c=C.selectTemplate(t,n,r,o);for(let u=0;u<e;u++)l.push(c)}return l}static applyLeaderPromotion(e,t,a){const i=e.length;if(i<4)return;const n=[];if(i>=4&&i<=6){const r=t.randomInt(0,i),o=C.promoteRarity(a,1);n.push({index:r,rarity:o})}else if(i>=7&&i<=9){const r=t.randomInt(0,i),o=C.promoteRarity(a,2);n.push({index:r,rarity:o})}else if(i>=10){const r=t.randomInt(0,i);let o=t.randomInt(0,i);for(;o===r&&i>1;)o=t.randomInt(0,i);n.push({index:r,rarity:C.promoteRarity(a,1)},{index:o,rarity:C.promoteRarity(a,2)})}for(const r of n){const o=e[r.index],l=o.name.toLowerCase().replace(/\s+/g,"-"),c=C.getTemplateById(l);if(c){const u=C.generate({seed:o.seed+"-promoted",templateId:c.id,rarity:r.rarity});e[r.index]=u}}}static calculateUpgradePoints(e){return e>=C.CR_TIERS.EPIC.min?6:e>=C.CR_TIERS.VERY_HIGH.min?5:e>=C.CR_TIERS.HIGH.min?4:e>=C.CR_TIERS.MEDIUM_HIGH.min?3:e>=C.CR_TIERS.MEDIUM.min?2:e>=C.CR_TIERS.LOW_MEDIUM.min?1:0}static upgradeRarity(e){return e==="common"?"uncommon":"elite"}static getRarityDistribution(e,t){const a=C.calculateUpgradePoints(t),i=Array(e).fill("common");for(let n=0;n<a;n++){const r=n%e;i[r]=C.upgradeRarity(i[r])}return i}static promoteRarity(e,t){const a=["common","uncommon","elite","boss"],i=a.indexOf(e),n=Math.min(i+t,a.length-1);return a[n]}};C.ARCHETYPE_TAGS={brute:["combat","damage","defense","melee","durability"],archer:["combat","ranged","accuracy","mobility","stealth"],support:["support","healing","buff","control","utility"]},C.MAX_AUDIO_INFLUENCE=2,C.BOSS_TITLES=["the Destroyer","the Unbreakable","the Annihilator","the Fearless","the Ancient","Lord of Ruin","the World-Ender","the Eternal","King of Ruin","the Indomitable","the Void Walker","Master of Destruction","the Fallen","the Ascendant","the Heartless","Sovereign of Pain","the Soul Render","the Undying","the Chaos Bringer","Lord of Death","the World Breaker","the Timeless","the Forgotten"],C.BOSS_PREFIXES=["Great","Eternal","Ancient","Dread","High","Dark","Void","Blood","Iron","Storm"],C.CR_TIERS={LOW:{min:0,max:2,upgrades:0},LOW_MEDIUM:{min:3,max:5,upgrades:1},MEDIUM:{min:6,max:10,upgrades:2},MEDIUM_HIGH:{min:11,max:15,upgrades:3},HIGH:{min:16,max:20,upgrades:4},VERY_HIGH:{min:21,max:30,upgrades:5},EPIC:{min:31,max:1/0,upgrades:6}};let Ke=C;class pt{run(e,t,a){const i=new Fs(a,e,t);let n=!1;for(let r=0;r<a.runCount;r++){if(a.abortSignal?.aborted){n=!0;break}const o=`${a.baseSeed}-${r}`,l=As(o),c=a.enemyRegeneration?Ke.generateEncounterByCR({...a.enemyRegeneration,seed:`${a.enemyRegeneration.seed}-${r}`}):t,d=new _s().runFullCombat(e,c,a.aiConfig,a.combatConfig,l);i.aggregateRun(d,r,o),a.onProgress&&a.onProgress(r+1,a.runCount)}return i.getResults(n)}}class Fs{constructor(e,t,a){this.playerWins=0,this.enemyWins=0,this.draws=0,this.roundsList=[],this.roundsOnWin=[],this.roundsOnLoss=[],this.playerHPPercentRemainingOnWin=[],this.playerDeathRounds=[],this.enemyDeathRounds=[],this.combatantAccumulators=new Map,this.runDetails=null,this.enemyGenTracker=new Map,this.config=e;const i=t.map(o=>o.level??1);this.party={memberCount:t.length,averageLevel:i.length>0?i.reduce((o,l)=>o+l,0)/i.length:0,memberNames:t.map(o=>o.name)};const n=a.map(o=>o.cr??o.level??1);this.encounter={enemyCount:a.length,averageCR:n.length>0?n.reduce((o,l)=>o+l,0)/n.length:0,enemyNames:a.map(o=>o.name)};const r=!!e.enemyRegeneration;this.combatantIds=[...t.map((o,l)=>({id:`player_${l}`,name:o.name,side:"player"})),...a.map((o,l)=>({id:`enemy_${t.length+l}`,name:r?`Enemy ${l+1}`:o.name,side:"enemy"}))];for(const{id:o,name:l,side:c}of this.combatantIds)this.combatantAccumulators.set(o,new Hs(o,l,c));e.collectDetailedLogs&&(this.runDetails=[])}aggregateRun(e,t,a){const{combat:i,result:n,metrics:r}=e;switch(n.winnerSide){case"player":this.playerWins++;break;case"enemy":this.enemyWins++;break;case"draw":this.draws++;break}const o=n.roundsElapsed;if(this.roundsList.push(o),n.winnerSide==="player"&&this.roundsOnWin.push(o),n.winnerSide==="enemy"&&this.roundsOnLoss.push(o),n.winnerSide==="player"&&e.combat){const l=e.combat.combatants.filter(c=>c.id.startsWith("player"));if(l.length>0){const c=l.reduce((d,h)=>d+h.character.hp.max,0),u=l.reduce((d,h)=>d+h.currentHP,0);c>0&&this.playerHPPercentRemainingOnWin.push(u/c)}}if(e.combat){for(const l of e.combat.combatants)if(l.isDefeated){const u=r.get(l.id)?.roundsSurvived??o;l.id.startsWith("player")?this.playerDeathRounds.push(u):this.enemyDeathRounds.push(u)}}for(const[l,c]of r){const u=this.combatantAccumulators.get(l);u&&u.accumulate(c,o)}if(this.config.enemyRegeneration&&i)for(const l of i.combatants){if(!l.id.startsWith("enemy"))continue;const c=l.character.name,u=this.enemyGenTracker.get(c)??{count:0,hps:[],acs:[],crs:[]};u.count++,u.hps.push(l.character.hp.max),u.acs.push(l.character.armor_class),u.crs.push(l.character.cr??l.character.level??1),this.enemyGenTracker.set(c,u)}this.runDetails!==null&&this.runDetails.push({runIndex:t,seed:a,combat:i,result:n,metrics:new Map(r)})}getResults(e=!1){const t=this.roundsList.length,a={totalRuns:t,playerWins:this.playerWins,enemyWins:this.enemyWins,draws:this.draws,playerWinRate:t>0?this.playerWins/t:0,averageRounds:this.average(this.roundsList),medianRounds:this.median(this.roundsList),averageRoundsOnWin:this.average(this.roundsOnWin),averageRoundsOnLoss:this.average(this.roundsOnLoss),averagePlayerHPPercentRemaining:this.average(this.playerHPPercentRemainingOnWin)*100,totalPlayerDeaths:this.playerDeathRounds.length,averageRoundsPerPlayerDeath:this.average(this.playerDeathRounds),totalEnemyDeaths:this.enemyDeathRounds.length,averageRoundsPerEnemyDeath:this.average(this.enemyDeathRounds)},i=new Map;for(const[r,o]of this.combatantAccumulators)i.set(r,o.toSimulationMetrics(t));let n;return this.enemyGenTracker.size>0&&(n=Array.from(this.enemyGenTracker.entries()).sort((r,o)=>o[1].count-r[1].count).map(([r,o])=>({name:r,count:o.count,hpRange:{min:Math.min(...o.hps),max:Math.max(...o.hps),avg:this.average(o.hps)},acRange:{min:Math.min(...o.acs),max:Math.max(...o.acs),avg:this.average(o.acs)},crRange:{min:Math.min(...o.crs),max:Math.max(...o.crs),avg:this.average(o.crs)}}))),{config:this.config,summary:a,party:this.party,encounter:this.encounter,perCombatantMetrics:i,enemyGenerationStats:n,runDetails:this.runDetails!==null&&this.runDetails.length>0?this.runDetails:void 0,wasCancelled:e}}average(e){return e.length===0?0:e.reduce((t,a)=>t+a,0)/e.length}median(e){if(e.length===0)return 0;const t=[...e].sort((i,n)=>i-n),a=Math.floor(t.length/2);return t.length%2===0?(t[a-1]+t[a])/2:t[a]}}class Hs{constructor(e,t,a){this.totalDamageDealtSum=0,this.totalDamageTakenSum=0,this.totalHealingDoneSum=0,this.roundsSurvivedSum=0,this.survivalCount=0,this.killCount=0,this.criticalHitSum=0,this.hitsSum=0,this.missesSum=0,this.totalAttackActions=0,this.spellsCastSum=0,this.actionTypeCounts=new Map,this.damagePerRoundList=[],this.hpRemainingList=[],this.id=e,this.name=t,this.side=a}accumulate(e,t){this.totalDamageDealtSum+=e.totalDamageDealt,this.totalDamageTakenSum+=e.totalDamageTaken,this.totalHealingDoneSum+=e.totalHealingDone,this.roundsSurvivedSum+=e.roundsSurvived,e.survived&&this.survivalCount++;const a=e.actionsByType.attack??0;this.criticalHitSum+=e.criticalHits,this.hitsSum+=e.hits,this.missesSum+=e.misses,this.killCount+=e.kills,this.totalAttackActions+=a,this.spellsCastSum+=e.spellsCast;for(const[i,n]of Object.entries(e.actionsByType))this.actionTypeCounts.set(i,(this.actionTypeCounts.get(i)??0)+n);if(e.roundsSurvived>0&&e.damagePerRound.length>0){const i=e.damagePerRound.reduce((n,r)=>n+r,0)/e.damagePerRound.length;this.damagePerRoundList.push(i)}e.survived&&e.hpRemainingPercent>0&&this.hpRemainingList.push(e.hpRemainingPercent)}toSimulationMetrics(e){const t=e>0?e:1;let a="attack",i=0;for(const[o,l]of this.actionTypeCounts)l>i&&(i=l,a=o);const n=xi(this.damagePerRoundList,20),r=xi(this.hpRemainingList,20);return{combatantId:this.id,name:this.name,side:this.side,averageDamagePerRound:this.average(this.damagePerRoundList),medianDamagePerRound:this.median(this.damagePerRoundList),averageTotalDamageDealt:this.totalDamageDealtSum/t,averageTotalDamageTaken:this.totalDamageTakenSum/t,averageHealingDone:this.totalHealingDoneSum/t,averageRoundsSurvived:this.roundsSurvivedSum/t,survivalRate:this.survivalCount/t,killRate:this.killCount/t,criticalHitRate:this.totalAttackActions>0?this.criticalHitSum/this.totalAttackActions:0,averageHitRate:this.hitsSum+this.missesSum>0?this.hitsSum/(this.hitsSum+this.missesSum):0,averageHitsPerRun:this.hitsSum/t,averageMissesPerRun:this.missesSum/t,averageSpellSlotsUsed:this.spellsCastSum/t,mostUsedAction:a,damageDistribution:n,hpRemainingDistribution:r}}average(e){return e.length===0?0:e.reduce((t,a)=>t+a,0)/e.length}median(e){if(e.length===0)return 0;const t=[...e].sort((i,n)=>i-n),a=Math.floor(t.length/2);return t.length%2===0?(t[a-1]+t[a])/2:t[a]}}function xi(s,e=20){if(s.length===0)return[];const t=Math.min(...s),a=Math.max(...s);if(t===a)return[{rangeStart:t,rangeEnd:a,count:s.length,percent:100}];const n=(a-t)/e,r=[];for(let o=0;o<e;o++)r.push({rangeStart:t+o*n,rangeEnd:t+(o+1)*n,count:0});for(const o of s){const l=Math.min(o,a);let c=Math.floor((l-t)/n);c>=e&&(c=e-1),r[c].count++}return r.map(o=>({rangeStart:o.rangeStart,rangeEnd:o.rangeEnd,count:o.count,percent:s.length>0?o.count/s.length*100:0}))}const Pa={easy:{min:.9,max:1},medium:{min:.7,max:.8},hard:{min:.5,max:.6},deadly:{min:.3,max:.4}};class ro{validate(e,t,a,i){const r=new pt().run(e,t,i);return this.analyze(r,a)}analyze(e,t){const{summary:a}=e,i=a.playerWinRate,n=Pa[t],r=this.classifyDifficulty(i),o=this.calculateBalanceScore(i,n),l=this.classifyVariance(i,n),c=this.calculateConfidence(a.totalRuns),u=this.generateRecommendations(i,n,r,t,c,a);return{intendedDifficulty:t,actualDifficulty:r,balanceScore:o,playerWinRate:i,expectedWinRate:n,difficultyVariance:l,confidence:c,recommendations:u,averagePlayerHPPercentRemaining:a.averagePlayerHPPercentRemaining,totalRuns:a.totalRuns}}classifyDifficulty(e){return e>=.9?"easy":e>=.7?"medium":e>=.5?"hard":"deadly"}calculateBalanceScore(e,t){const a=(t.min+t.max)/2,i=(t.max-t.min)/2,n=Math.abs(e-a),r=i>0?n/i:0;if(n<=i){const u=70+30*(1-r);return Math.round(Math.min(100,Math.max(0,u)))}const o=n-i,c=70-Math.min(70,o/.5*70);return Math.round(Math.min(100,Math.max(0,c)))}classifyVariance(e,t){return e>t.max?"underpowered":e<t.min?"overpowered":"balanced"}calculateConfidence(e){return e<=0?0:Math.min(1,1-1/Math.sqrt(e))}generateRecommendations(e,t,a,i,n,r){const o=[],l=e>t.max;if(e<t.min){const u=t.min-e,d=r.totalEnemyDeaths>0?Math.round(r.totalEnemyDeaths/r.totalRuns):1;u>.3?(o.push({description:"Reduce enemy CR by 1-2 levels",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),o.push({description:`Reduce enemy count by ${Math.max(1,Math.ceil(d*.3))}`,expectedImpact:`+${Math.round(u*30)}-${Math.round(u*50)}% player win rate`,confidence:n})):u>.15?o.push({description:"Reduce enemy CR by 1 level",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}):o.push({description:"Reduce enemy CR by 1 or remove one enemy ability",expectedImpact:`+${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),r.totalPlayerDeaths/r.totalRuns>2&&o.push({description:"Consider removing legendary actions or reducing legendary resistance count",expectedImpact:"+5-15% player win rate",confidence:n})}else if(l){const u=e-t.max;u>.15?(o.push({description:"Increase enemy CR by 1-2 levels",expectedImpact:`-${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n}),o.push({description:"Add 1-2 additional enemies",expectedImpact:`-${Math.round(u*30)}-${Math.round(u*50)}% player win rate`,confidence:n})):o.push({description:"Increase enemy CR by 1 level or add one more enemy",expectedImpact:`-${Math.round(u*40)}-${Math.round(u*60)}% player win rate`,confidence:n})}else r.averagePlayerHPPercentRemaining>80&&e>t.min+.05?o.push({description:"Encounter is well-balanced. Consider increasing difficulty slightly if players are ending with high HP.",expectedImpact:"-5-10% player win rate",confidence:n}):r.averagePlayerHPPercentRemaining<30&&e<t.max-.05?o.push({description:"Encounter is balanced but punishing. Consider a small reduction in enemy damage if players are consistently low on HP.",expectedImpact:"+5-10% player HP remaining",confidence:n}):o.push({description:"Encounter is well-balanced for the intended difficulty. No changes needed.",expectedImpact:"None — encounter is within target range",confidence:n});return o}}class oo{sweep(e,t,a,i){const n=this.generateValues(a.range),r=n.length,o=[];let l=!1;for(let c=0;c<r;c++){if(a.abortSignal?.aborted){l=!0;break}const u=n[c];try{const{modifiedPlayers:d,modifiedEnemies:h}=this.applyParameter(e,t,a.variable,u);if(d.length===0||h.length===0){o.push({parameterValue:u,playerWinRate:0,averageRounds:0,averageHPRemaining:0,totalPlayerDeaths:0,totalEnemyDeaths:0,medianRounds:0}),i?.(c+1,r);continue}const p=`${a.baseSeed??"sweep"}-${u}`,m=new pt().run(d,h,{runCount:a.simulationsPerPoint,baseSeed:p,aiConfig:a.aiConfig,combatConfig:a.combatConfig,collectDetailedLogs:!1,abortSignal:a.abortSignal});m.wasCancelled?(l=!0,o.push(this.summarizeToPoint(u,m.summary))):o.push(this.summarizeToPoint(u,m.summary))}catch{o.push({parameterValue:u,playerWinRate:0,averageRounds:0,averageHPRemaining:0,totalPlayerDeaths:0,totalEnemyDeaths:0,medianRounds:0})}i?.(c+1,r)}return{variable:a.variable,range:a.range,simulationsPerPoint:a.simulationsPerPoint,dataPoints:o,wasCancelled:l}}generateValues(e){const t=[];for(let a=e.min;a<=e.max+e.step*.5;a+=e.step){const i=Math.round(a*1e3)/1e3;if(i>e.max+e.step*.01)break;t.push(i)}return t}applyParameter(e,t,a,i){switch(a){case"cr":return this.applyCR(e,t,i);case"enemyCount":return this.applyEnemyCount(e,t,i);case"partyLevel":return this.applyPartyLevel(e,t,i);case"difficultyMultiplier":return this.applyDifficultyMultiplier(e,t,i);case"rarity":return this.applyRarity(e,t,i);case"hpLevel":return this.applyStatLevel(e,t,{hpLevel:i});case"attackLevel":return this.applyStatLevel(e,t,{attackLevel:i});case"defenseLevel":return this.applyStatLevel(e,t,{defenseLevel:i});default:return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(t,1)}}}applyCR(e,t,a){const i={...t,cr:a};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}applyEnemyCount(e,t,a){const i=Math.max(1,Math.round(a));return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(t,i)}}applyPartyLevel(e,t,a){const i=Math.max(1,Math.round(a));return{modifiedPlayers:e.map((r,o)=>({...r,level:i,hp:{...r.hp,current:Math.round(r.hp.current/Math.max(1,r.level)*i),max:Math.round(r.hp.max/Math.max(1,r.level)*i)}})),modifiedEnemies:this.generateEnemies(t,1)}}applyDifficultyMultiplier(e,t,a){const i={...t,difficultyMultiplier:a};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}applyRarity(e,t,a){const i=["common","uncommon","elite","boss"],n=Math.max(0,Math.min(i.length-1,Math.round(a))),r={...t,rarity:i[n]};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(r,1)}}applyStatLevel(e,t,a){const i={...t,statLevels:{...t.statLevels,...a}};return{modifiedPlayers:e,modifiedEnemies:this.generateEnemies(i,1)}}generateEnemies(e,t){const a=[],i=e.templateId??e.category??"enemy";for(let n=0;n<t;n++){const r=`${i}-sweep-${n}`,o=Ke.generate({seed:r,cr:e.cr,rarity:e.rarity,category:e.category,archetype:e.archetype,templateId:e.templateId,statLevels:e.statLevels,difficultyMultiplier:e.difficultyMultiplier});a.push(o)}return a}summarizeToPoint(e,t){return{parameterValue:e,playerWinRate:t.playerWinRate,averageRounds:t.averageRounds,averageHPRemaining:t.averagePlayerHPPercentRemaining,totalPlayerDeaths:t.totalPlayerDeaths,totalEnemyDeaths:t.totalEnemyDeaths,medianRounds:t.medianRounds}}}class lo{compare(e,t,a){const i=e.label??"Config A",n=t.label??"Config B",r=this.runSimulations(e,a,`${a.baseSeed}-A`,i,a.onProgress),o=this.runSimulations(t,a,`${a.baseSeed}-B`,n,a.onProgress),l=this.calculateDeltas(r.summary,o.summary),c=this.calculateCombatantDeltas(r.perCombatantMetrics,o.perCombatantMetrics),u=a.significanceThreshold??.05,d=this.testSignificance(r.summary,o.summary,u),h=r.wasCancelled||o.wasCancelled;return{labelA:i,labelB:n,resultsA:r,resultsB:o,summaryA:r.summary,summaryB:o.summary,deltas:l,combatantDeltas:c,winRateSignificance:d,wasCancelled:h}}runSimulations(e,t,a,i,n){return new pt().run(e.players,e.enemies,{runCount:t.runCount,baseSeed:a,aiConfig:t.aiConfig,combatConfig:e.combatConfig??t.combatConfig,collectDetailedLogs:!1,abortSignal:t.abortSignal,onProgress:n?(o,l)=>n(o,l,i):void 0})}calculateDeltas(e,t){return{winRateDelta:this.round(e.playerWinRate-t.playerWinRate),averageRoundsDelta:this.round(e.averageRounds-t.averageRounds),averageHPRemainingDelta:this.round(e.averagePlayerHPPercentRemaining-t.averagePlayerHPPercentRemaining),totalPlayerDeathsDelta:e.totalPlayerDeaths-t.totalPlayerDeaths,totalEnemyDeathsDelta:e.totalEnemyDeaths-t.totalEnemyDeaths,medianRoundsDelta:e.medianRounds-t.medianRounds}}calculateCombatantDeltas(e,t){const a=[],i=this.getMetricsBySide(e,"player"),n=this.getMetricsBySide(t,"player"),r=this.getMetricsBySide(e,"enemy"),o=this.getMetricsBySide(t,"enemy"),l=Math.min(i.length,n.length);for(let u=0;u<l;u++)a.push(this.makeCombatantDelta(i[u],n[u]));for(let u=l;u<i.length;u++)a.push(this.makeCombatantDelta(i[u],null));for(let u=l;u<n.length;u++)a.push(this.makeCombatantDelta(null,n[u]));const c=Math.min(r.length,o.length);for(let u=0;u<c;u++)a.push(this.makeCombatantDelta(r[u],o[u]));for(let u=c;u<r.length;u++)a.push(this.makeCombatantDelta(r[u],null));for(let u=c;u<o.length;u++)a.push(this.makeCombatantDelta(null,o[u]));return a}getMetricsBySide(e,t){const a=[];for(const i of e.values())i.side===t&&a.push(i);return a}makeCombatantDelta(e,t){return e&&t?{name:e.name,side:e.side,dprDelta:this.round(e.averageDamagePerRound-t.averageDamagePerRound),damageDealtDelta:this.round(e.averageTotalDamageDealt-t.averageTotalDamageDealt),damageTakenDelta:this.round(e.averageTotalDamageTaken-t.averageTotalDamageTaken),survivalRateDelta:this.round(e.survivalRate-t.survivalRate),killRateDelta:this.round(e.killRate-t.killRate),criticalHitRateDelta:this.round(e.criticalHitRate-t.criticalHitRate),healingDoneDelta:this.round(e.averageHealingDone-t.averageHealingDone)}:e?{name:`${e.name} (only in A)`,side:e.side,dprDelta:e.averageDamagePerRound,damageDealtDelta:e.averageTotalDamageDealt,damageTakenDelta:e.averageTotalDamageTaken,survivalRateDelta:e.survivalRate,killRateDelta:e.killRate,criticalHitRateDelta:e.criticalHitRate,healingDoneDelta:e.averageHealingDone}:{name:`${t.name} (only in B)`,side:t.side,dprDelta:-t.averageDamagePerRound,damageDealtDelta:-t.averageTotalDamageDealt,damageTakenDelta:-t.averageTotalDamageTaken,survivalRateDelta:-t.survivalRate,killRateDelta:-t.killRate,criticalHitRateDelta:-t.criticalHitRate,healingDoneDelta:-t.averageHealingDone}}testSignificance(e,t,a){const i=Math.min(e.totalRuns,t.totalRuns);if(i===0)return{isSignificant:!1,pValue:1,threshold:a,interpretation:"No simulation data available"};const n=e.playerWinRate,r=t.playerWinRate,o=Math.abs(n-r);if(o===0)return{isSignificant:!1,pValue:1,threshold:a,interpretation:"No difference in win rates between configurations"};const l=n*(1-n)/i,c=r*(1-r)/i,u=Math.sqrt(l+c);if(u===0)return{isSignificant:o>0,pValue:o>0?0:1,threshold:a,interpretation:o>0?"One configuration wins all runs — difference is definitive":"Both configurations have identical outcomes"};const d=o/u,h=this.twoTailedPValue(d),p=h<a;let g;if(p){const m=n>r?"A":"B",f=(o*100).toFixed(1);g=`Config ${m} has a statistically significant ${f}% higher win rate (p=${h.toFixed(4)}, n=${i})`}else g=`The ${(o*100).toFixed(1)}% win rate difference is not statistically significant (p=${h.toFixed(4)}, n=${i}). More runs may be needed to detect a real difference.`;return{isSignificant:p,pValue:h,threshold:a,interpretation:g}}twoTailedPValue(e){const t=this.normalCDF(Math.abs(e));return Math.max(0,2*(1-t))}normalCDF(e){const t=.254829592,a=-.284496736,i=1.421413741,n=-1.453152027,r=1.061405429,o=.3275911,l=e<0?-1:1;e=Math.abs(e)/Math.SQRT2;const c=1/(1+o*e),u=1-((((r*c+n)*c+i)*c+a)*c+t)*c*Math.exp(-e*e);return .5*(1+l*u)}round(e){return Math.round(e*1e4)/1e4}}const oe=class oe{suggest(e,t,a,i){const{aiConfig:n,combatConfig:r,baseSeed:o="difficulty-calc",simulationsPerProbe:l=oe.DEFAULT_SIMULATIONS_PER_PROBE,maxIterations:c=oe.DEFAULT_MAX_ITERATIONS,enemyCount:u=1,abortSignal:d,onProgress:h}=i,p=[];let g=0;const m=e.map(O=>O.level),f=Wt(m,a),v=ot(u),w=f/v,E=Ta(w);let I=oe.CR_FLOOR,D=oe.CR_CEILING,x=Math.max(I,Math.min(D,E));const N=Pa[a];let $=!1,H=null,te=!1;for(let O=0;O<c;O++){if(d?.aborted){te=!0;break}h?.(O+1,c,x);const P=this.runProbe(e,t,x,u,n,r,l,o,O);g+=l;const L={cr:x,winRate:P.playerWinRate,totalRuns:P.totalRuns,averageRounds:P.averageRounds,averageHPRemaining:P.averagePlayerHPPercentRemaining};p.push(L);const _e=(N.min+N.max)/2;if((!H||Math.abs(L.winRate-_e)<Math.abs(H.winRate-_e))&&(H=L),L.winRate>=N.min&&L.winRate<=N.max){$=!0;break}L.winRate>N.max?I=x:D=x;const ge=(I+D)/2;if(Math.abs(ge-x)<oe.CR_STEP_MIN){if(L.winRate>N.max?x=Math.min(oe.CR_CEILING,x+oe.CR_STEP_MIN):x=Math.max(oe.CR_FLOOR,x-oe.CR_STEP_MIN),x===p[p.length-1].cr)break}else x=ge}const Q=H?.cr??x,X=this.generateEnemy(t,Q,o),de=this.calculateMarginOfError(H?.totalRuns??l),ee=this.formatConfidenceInterval(H?.winRate??0,de);return{targetDifficulty:a,recommendedCR:this.roundCR(Q),winRate:H?.winRate??0,expectedWinRateRange:N,confidenceInterval:ee,marginOfError:de,converged:$,totalSimulationsRun:g,iterationsUsed:p.length,probes:p,initialCREstimate:this.roundCR(E),suggestedEnemy:X,wasCancelled:te}}runProbe(e,t,a,i,n,r,o,l,c){const u=this.generateEnemies(t,a,i,l),d=new pt;try{return d.run(e,u,{runCount:o,baseSeed:`${l}-probe-${c}`,aiConfig:n,combatConfig:r,collectDetailedLogs:!1}).summary}catch{return{totalRuns:o,playerWins:0,enemyWins:o,draws:0,playerWinRate:0,averageRounds:0,medianRounds:0,averageRoundsOnWin:0,averageRoundsOnLoss:0,averagePlayerHPPercentRemaining:0,totalPlayerDeaths:o*e.length,averageRoundsPerPlayerDeath:0,totalEnemyDeaths:0,averageRoundsPerEnemyDeath:0}}}generateEnemies(e,t,a,i){const n=[];for(let r=0;r<a;r++)n.push(this.generateEnemy(e,t,`${i}-enemy-${r}`));return n}generateEnemy(e,t,a){return Ke.generate({seed:a,cr:t,rarity:e.rarity??"elite",category:e.category??"humanoid",archetype:e.archetype??"brute",templateId:e.templateId,statLevels:e.statLevels,difficultyMultiplier:e.difficultyMultiplier})}calculateMarginOfError(e){return e<=0?1:1.96*Math.sqrt(.5*.5/e)}formatConfidenceInterval(e,t){const a=Math.round(e*100),i=Math.round(t*100);return`${a}% ± ${i}%`}roundCR(e){return e<=0?0:e<.25?.125:e<.375?.25:e<.75?.5:Math.round(e)}};oe.DEFAULT_SIMULATIONS_PER_PROBE=200,oe.DEFAULT_MAX_ITERATIONS=10,oe.CR_STEP_MIN=.25,oe.CR_FLOOR=.125,oe.CR_CEILING=30;let ca=oe;const co=s=>s*5,Me={baseXP:{perfect:10,great:7,good:5,ok:2,miss:0,wrongKey:0},xpRatio:.1,combo:{enabled:!0,cap:5,okBreaksCombo:!0,endBonus:{enabled:!0,formula:co}},groove:{perHitMultiplier:!1,perHitScale:1,endBonus:{enabled:!0,maxStreakWeight:.5,avgHotnessWeight:.5,durationWeight:.25}},maxMultiplier:5};function uo(s){const e=Me.combo;return s?{enabled:s.enabled??e.enabled,cap:s.cap??e.cap,formula:s.formula??e.formula,okBreaksCombo:s.okBreaksCombo??e.okBreaksCombo,endBonus:{...e.endBonus,...s.endBonus}}:e}function ho(s){const e=Me.groove;return s?{perHitMultiplier:s.perHitMultiplier??e.perHitMultiplier,perHitScale:s.perHitScale??e.perHitScale,endBonus:{...e.endBonus,...s.endBonus}}:e}function Pt(s){return s?{baseXP:{...Me.baseXP,...s.baseXP},xpRatio:s.xpRatio??Me.xpRatio,combo:uo(s.combo),groove:ho(s.groove),maxMultiplier:s.maxMultiplier??Me.maxMultiplier}:Me}function po(s,e=Me.combo.okBreaksCombo){return s==="miss"||s==="wrongKey"?!0:s==="ok"?e:!1}function b(s,e,t){function a(o,l){if(o._zod||Object.defineProperty(o,"_zod",{value:{def:l,constr:r,traits:new Set},enumerable:!1}),o._zod.traits.has(s))return;o._zod.traits.add(s),e(o,l);const c=r.prototype,u=Object.keys(c);for(let d=0;d<u.length;d++){const h=u[d];h in o||(o[h]=c[h].bind(o))}}const i=t?.Parent??Object;class n extends i{}Object.defineProperty(n,"name",{value:s});function r(o){var l;const c=t?.Parent?new n:this;a(c,o),(l=c._zod).deferred??(l.deferred=[]);for(const u of c._zod.deferred)u();return c}return Object.defineProperty(r,"init",{value:a}),Object.defineProperty(r,Symbol.hasInstance,{value:o=>t?.Parent&&o instanceof t.Parent?!0:o?._zod?.traits?.has(s)}),Object.defineProperty(r,"name",{value:s}),r}class Ye extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}class Ws extends Error{constructor(e){super(`Encountered unidirectional transform during encode: ${e}`),this.name="ZodEncodeError"}}const zs={};function xe(s){return zs}function qs(s){const e=Object.values(s).filter(a=>typeof a=="number");return Object.entries(s).filter(([a,i])=>e.indexOf(+a)===-1).map(([a,i])=>i)}function ua(s,e){return typeof e=="bigint"?e.toString():e}function xa(s){return{get value(){{const e=s();return Object.defineProperty(this,"value",{value:e}),e}}}}function Na(s){return s==null}function $a(s){const e=s.startsWith("^")?1:0,t=s.endsWith("$")?s.length-1:s.length;return s.slice(e,t)}function mo(s,e){const t=(s.toString().split(".")[1]||"").length,a=e.toString();let i=(a.split(".")[1]||"").length;if(i===0&&/\d?e-\d?/.test(a)){const l=a.match(/\d?e-(\d?)/);l?.[1]&&(i=Number.parseInt(l[1]))}const n=t>i?t:i,r=Number.parseInt(s.toFixed(n).replace(".","")),o=Number.parseInt(e.toFixed(n).replace(".",""));return r%o/10**n}const Ni=Symbol("evaluating");function F(s,e,t){let a;Object.defineProperty(s,e,{get(){if(a!==Ni)return a===void 0&&(a=Ni,a=t()),a},set(i){Object.defineProperty(s,e,{value:i})},configurable:!0})}function He(s,e,t){Object.defineProperty(s,e,{value:t,writable:!0,enumerable:!0,configurable:!0})}function $e(...s){const e={};for(const t of s){const a=Object.getOwnPropertyDescriptors(t);Object.assign(e,a)}return Object.defineProperties({},e)}function $i(s){return JSON.stringify(s)}function go(s){return s.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}const Us="captureStackTrace"in Error?Error.captureStackTrace:(...s)=>{};function xt(s){return typeof s=="object"&&s!==null&&!Array.isArray(s)}const fo=xa(()=>{if(typeof navigator<"u"&&navigator?.userAgent?.includes("Cloudflare"))return!1;try{const s=Function;return new s(""),!0}catch{return!1}});function Je(s){if(xt(s)===!1)return!1;const e=s.constructor;if(e===void 0||typeof e!="function")return!0;const t=e.prototype;return!(xt(t)===!1||Object.prototype.hasOwnProperty.call(t,"isPrototypeOf")===!1)}function Gs(s){return Je(s)?{...s}:Array.isArray(s)?[...s]:s}const yo=new Set(["string","number","symbol"]);function qt(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Le(s,e,t){const a=new s._zod.constr(e??s._zod.def);return(!e||t?.parent)&&(a._zod.parent=s),a}function M(s){const e=s;if(!e)return{};if(typeof e=="string")return{error:()=>e};if(e?.message!==void 0){if(e?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");e.error=e.message}return delete e.message,typeof e.error=="string"?{...e,error:()=>e.error}:e}function vo(s){return Object.keys(s).filter(e=>s[e]._zod.optin==="optional"&&s[e]._zod.optout==="optional")}const bo={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function wo(s,e){const t=s._zod.def,a=t.checks;if(a&&a.length>0)throw new Error(".pick() cannot be used on object schemas containing refinements");const n=$e(s._zod.def,{get shape(){const r={};for(const o in e){if(!(o in t.shape))throw new Error(`Unrecognized key: "${o}"`);e[o]&&(r[o]=t.shape[o])}return He(this,"shape",r),r},checks:[]});return Le(s,n)}function So(s,e){const t=s._zod.def,a=t.checks;if(a&&a.length>0)throw new Error(".omit() cannot be used on object schemas containing refinements");const n=$e(s._zod.def,{get shape(){const r={...s._zod.def.shape};for(const o in e){if(!(o in t.shape))throw new Error(`Unrecognized key: "${o}"`);e[o]&&delete r[o]}return He(this,"shape",r),r},checks:[]});return Le(s,n)}function _o(s,e){if(!Je(e))throw new Error("Invalid input to extend: expected a plain object");const t=s._zod.def.checks;if(t&&t.length>0){const n=s._zod.def.shape;for(const r in e)if(Object.getOwnPropertyDescriptor(n,r)!==void 0)throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}const i=$e(s._zod.def,{get shape(){const n={...s._zod.def.shape,...e};return He(this,"shape",n),n}});return Le(s,i)}function Ao(s,e){if(!Je(e))throw new Error("Invalid input to safeExtend: expected a plain object");const t=$e(s._zod.def,{get shape(){const a={...s._zod.def.shape,...e};return He(this,"shape",a),a}});return Le(s,t)}function ko(s,e){const t=$e(s._zod.def,{get shape(){const a={...s._zod.def.shape,...e._zod.def.shape};return He(this,"shape",a),a},get catchall(){return e._zod.def.catchall},checks:[]});return Le(s,t)}function To(s,e,t){const i=e._zod.def.checks;if(i&&i.length>0)throw new Error(".partial() cannot be used on object schemas containing refinements");const r=$e(e._zod.def,{get shape(){const o=e._zod.def.shape,l={...o};if(t)for(const c in t){if(!(c in o))throw new Error(`Unrecognized key: "${c}"`);t[c]&&(l[c]=s?new s({type:"optional",innerType:o[c]}):o[c])}else for(const c in o)l[c]=s?new s({type:"optional",innerType:o[c]}):o[c];return He(this,"shape",l),l},checks:[]});return Le(e,r)}function Co(s,e,t){const a=$e(e._zod.def,{get shape(){const i=e._zod.def.shape,n={...i};if(t)for(const r in t){if(!(r in n))throw new Error(`Unrecognized key: "${r}"`);t[r]&&(n[r]=new s({type:"nonoptional",innerType:i[r]}))}else for(const r in i)n[r]=new s({type:"nonoptional",innerType:i[r]});return He(this,"shape",n),n}});return Le(e,a)}function je(s,e=0){if(s.aborted===!0)return!0;for(let t=e;t<s.issues.length;t++)if(s.issues[t]?.continue!==!0)return!0;return!1}function Ve(s,e){return e.map(t=>{var a;return(a=t).path??(a.path=[]),t.path.unshift(s),t})}function At(s){return typeof s=="string"?s:s?.message}function Ne(s,e,t){const a={...s,path:s.path??[]};if(!s.message){const i=At(s.inst?._zod.def?.error?.(s))??At(e?.error?.(s))??At(t.customError?.(s))??At(t.localeError?.(s))??"Invalid input";a.message=i}return delete a.inst,delete a.continue,e?.reportInput||delete a.input,a}function La(s){return Array.isArray(s)?"array":typeof s=="string"?"string":"unknown"}function ut(...s){const[e,t,a]=s;return typeof e=="string"?{message:e,code:"custom",input:t,inst:a}:{...e}}const js=(s,e)=>{s.name="$ZodError",Object.defineProperty(s,"_zod",{value:s._zod,enumerable:!1}),Object.defineProperty(s,"issues",{value:e,enumerable:!1}),s.message=JSON.stringify(e,ua,2),Object.defineProperty(s,"toString",{value:()=>s.message,enumerable:!1})},Vs=b("$ZodError",js),Ys=b("$ZodError",js,{Parent:Error});function Io(s,e=t=>t.message){const t={},a=[];for(const i of s.issues)i.path.length>0?(t[i.path[0]]=t[i.path[0]]||[],t[i.path[0]].push(e(i))):a.push(e(i));return{formErrors:a,fieldErrors:t}}function Eo(s,e=t=>t.message){const t={_errors:[]},a=i=>{for(const n of i.issues)if(n.code==="invalid_union"&&n.errors.length)n.errors.map(r=>a({issues:r}));else if(n.code==="invalid_key")a({issues:n.issues});else if(n.code==="invalid_element")a({issues:n.issues});else if(n.path.length===0)t._errors.push(e(n));else{let r=t,o=0;for(;o<n.path.length;){const l=n.path[o];o===n.path.length-1?(r[l]=r[l]||{_errors:[]},r[l]._errors.push(e(n))):r[l]=r[l]||{_errors:[]},r=r[l],o++}}};return a(s),t}const Oa=s=>(e,t,a,i)=>{const n=a?Object.assign(a,{async:!1}):{async:!1},r=e._zod.run({value:t,issues:[]},n);if(r instanceof Promise)throw new Ye;if(r.issues.length){const o=new(i?.Err??s)(r.issues.map(l=>Ne(l,n,xe())));throw Us(o,i?.callee),o}return r.value},Ba=s=>async(e,t,a,i)=>{const n=a?Object.assign(a,{async:!0}):{async:!0};let r=e._zod.run({value:t,issues:[]},n);if(r instanceof Promise&&(r=await r),r.issues.length){const o=new(i?.Err??s)(r.issues.map(l=>Ne(l,n,xe())));throw Us(o,i?.callee),o}return r.value},Ut=s=>(e,t,a)=>{const i=a?{...a,async:!1}:{async:!1},n=e._zod.run({value:t,issues:[]},i);if(n instanceof Promise)throw new Ye;return n.issues.length?{success:!1,error:new(s??Vs)(n.issues.map(r=>Ne(r,i,xe())))}:{success:!0,data:n.value}},Mo=Ut(Ys),Gt=s=>async(e,t,a)=>{const i=a?Object.assign(a,{async:!0}):{async:!0};let n=e._zod.run({value:t,issues:[]},i);return n instanceof Promise&&(n=await n),n.issues.length?{success:!1,error:new s(n.issues.map(r=>Ne(r,i,xe())))}:{success:!0,data:n.value}},Do=Gt(Ys),Ro=s=>(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Oa(s)(e,t,i)},Po=s=>(e,t,a)=>Oa(s)(e,t,a),xo=s=>async(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Ba(s)(e,t,i)},No=s=>async(e,t,a)=>Ba(s)(e,t,a),$o=s=>(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Ut(s)(e,t,i)},Lo=s=>(e,t,a)=>Ut(s)(e,t,a),Oo=s=>async(e,t,a)=>{const i=a?Object.assign(a,{direction:"backward"}):{direction:"backward"};return Gt(s)(e,t,i)},Bo=s=>async(e,t,a)=>Gt(s)(e,t,a),Fo=/^[cC][^\s-]{8,}$/,Ho=/^[0-9a-z]+$/,Wo=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,zo=/^[0-9a-vA-V]{20}$/,qo=/^[A-Za-z0-9]{27}$/,Uo=/^[a-zA-Z0-9_-]{21}$/,Go=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,jo=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,Li=s=>s?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${s}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,Vo=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,Yo="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";function Zo(){return new RegExp(Yo,"u")}const Xo=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Ko=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,Jo=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,Qo=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,el=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Zs=/^[A-Za-z0-9_-]*$/,tl=/^\+[1-9]\d{6,14}$/,Xs="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",al=new RegExp(`^${Xs}$`);function Ks(s){const e="(?:[01]\\d|2[0-3]):[0-5]\\d";return typeof s.precision=="number"?s.precision===-1?`${e}`:s.precision===0?`${e}:[0-5]\\d`:`${e}:[0-5]\\d\\.\\d{${s.precision}}`:`${e}(?::[0-5]\\d(?:\\.\\d+)?)?`}function il(s){return new RegExp(`^${Ks(s)}$`)}function sl(s){const e=Ks({precision:s.precision}),t=["Z"];s.local&&t.push(""),s.offset&&t.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");const a=`${e}(?:${t.join("|")})`;return new RegExp(`^${Xs}T(?:${a})$`)}const nl=s=>{const e=s?`[\\s\\S]{${s?.minimum??0},${s?.maximum??""}}`:"[\\s\\S]*";return new RegExp(`^${e}$`)},rl=/^-?\d+$/,Js=/^-?\d+(?:\.\d+)?$/,ol=/^(?:true|false)$/i,ll=/^[^A-Z]*$/,cl=/^[^a-z]*$/,me=b("$ZodCheck",(s,e)=>{var t;s._zod??(s._zod={}),s._zod.def=e,(t=s._zod).onattach??(t.onattach=[])}),Qs={number:"number",bigint:"bigint",object:"date"},en=b("$ZodCheckLessThan",(s,e)=>{me.init(s,e);const t=Qs[typeof e.value];s._zod.onattach.push(a=>{const i=a._zod.bag,n=(e.inclusive?i.maximum:i.exclusiveMaximum)??Number.POSITIVE_INFINITY;e.value<n&&(e.inclusive?i.maximum=e.value:i.exclusiveMaximum=e.value)}),s._zod.check=a=>{(e.inclusive?a.value<=e.value:a.value<e.value)||a.issues.push({origin:t,code:"too_big",maximum:typeof e.value=="object"?e.value.getTime():e.value,input:a.value,inclusive:e.inclusive,inst:s,continue:!e.abort})}}),tn=b("$ZodCheckGreaterThan",(s,e)=>{me.init(s,e);const t=Qs[typeof e.value];s._zod.onattach.push(a=>{const i=a._zod.bag,n=(e.inclusive?i.minimum:i.exclusiveMinimum)??Number.NEGATIVE_INFINITY;e.value>n&&(e.inclusive?i.minimum=e.value:i.exclusiveMinimum=e.value)}),s._zod.check=a=>{(e.inclusive?a.value>=e.value:a.value>e.value)||a.issues.push({origin:t,code:"too_small",minimum:typeof e.value=="object"?e.value.getTime():e.value,input:a.value,inclusive:e.inclusive,inst:s,continue:!e.abort})}}),ul=b("$ZodCheckMultipleOf",(s,e)=>{me.init(s,e),s._zod.onattach.push(t=>{var a;(a=t._zod.bag).multipleOf??(a.multipleOf=e.value)}),s._zod.check=t=>{if(typeof t.value!=typeof e.value)throw new Error("Cannot mix number and bigint in multiple_of check.");(typeof t.value=="bigint"?t.value%e.value===BigInt(0):mo(t.value,e.value)===0)||t.issues.push({origin:typeof t.value,code:"not_multiple_of",divisor:e.value,input:t.value,inst:s,continue:!e.abort})}}),dl=b("$ZodCheckNumberFormat",(s,e)=>{me.init(s,e),e.format=e.format||"float64";const t=e.format?.includes("int"),a=t?"int":"number",[i,n]=bo[e.format];s._zod.onattach.push(r=>{const o=r._zod.bag;o.format=e.format,o.minimum=i,o.maximum=n,t&&(o.pattern=rl)}),s._zod.check=r=>{const o=r.value;if(t){if(!Number.isInteger(o)){r.issues.push({expected:a,format:e.format,code:"invalid_type",continue:!1,input:o,inst:s});return}if(!Number.isSafeInteger(o)){o>0?r.issues.push({input:o,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:s,origin:a,inclusive:!0,continue:!e.abort}):r.issues.push({input:o,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:s,origin:a,inclusive:!0,continue:!e.abort});return}}o<i&&r.issues.push({origin:"number",input:o,code:"too_small",minimum:i,inclusive:!0,inst:s,continue:!e.abort}),o>n&&r.issues.push({origin:"number",input:o,code:"too_big",maximum:n,inclusive:!0,inst:s,continue:!e.abort})}}),hl=b("$ZodCheckMaxLength",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag.maximum??Number.POSITIVE_INFINITY;e.maximum<i&&(a._zod.bag.maximum=e.maximum)}),s._zod.check=a=>{const i=a.value;if(i.length<=e.maximum)return;const r=La(i);a.issues.push({origin:r,code:"too_big",maximum:e.maximum,inclusive:!0,input:i,inst:s,continue:!e.abort})}}),pl=b("$ZodCheckMinLength",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag.minimum??Number.NEGATIVE_INFINITY;e.minimum>i&&(a._zod.bag.minimum=e.minimum)}),s._zod.check=a=>{const i=a.value;if(i.length>=e.minimum)return;const r=La(i);a.issues.push({origin:r,code:"too_small",minimum:e.minimum,inclusive:!0,input:i,inst:s,continue:!e.abort})}}),ml=b("$ZodCheckLengthEquals",(s,e)=>{var t;me.init(s,e),(t=s._zod.def).when??(t.when=a=>{const i=a.value;return!Na(i)&&i.length!==void 0}),s._zod.onattach.push(a=>{const i=a._zod.bag;i.minimum=e.length,i.maximum=e.length,i.length=e.length}),s._zod.check=a=>{const i=a.value,n=i.length;if(n===e.length)return;const r=La(i),o=n>e.length;a.issues.push({origin:r,...o?{code:"too_big",maximum:e.length}:{code:"too_small",minimum:e.length},inclusive:!0,exact:!0,input:a.value,inst:s,continue:!e.abort})}}),jt=b("$ZodCheckStringFormat",(s,e)=>{var t,a;me.init(s,e),s._zod.onattach.push(i=>{const n=i._zod.bag;n.format=e.format,e.pattern&&(n.patterns??(n.patterns=new Set),n.patterns.add(e.pattern))}),e.pattern?(t=s._zod).check??(t.check=i=>{e.pattern.lastIndex=0,!e.pattern.test(i.value)&&i.issues.push({origin:"string",code:"invalid_format",format:e.format,input:i.value,...e.pattern?{pattern:e.pattern.toString()}:{},inst:s,continue:!e.abort})}):(a=s._zod).check??(a.check=()=>{})}),gl=b("$ZodCheckRegex",(s,e)=>{jt.init(s,e),s._zod.check=t=>{e.pattern.lastIndex=0,!e.pattern.test(t.value)&&t.issues.push({origin:"string",code:"invalid_format",format:"regex",input:t.value,pattern:e.pattern.toString(),inst:s,continue:!e.abort})}}),fl=b("$ZodCheckLowerCase",(s,e)=>{e.pattern??(e.pattern=ll),jt.init(s,e)}),yl=b("$ZodCheckUpperCase",(s,e)=>{e.pattern??(e.pattern=cl),jt.init(s,e)}),vl=b("$ZodCheckIncludes",(s,e)=>{me.init(s,e);const t=qt(e.includes),a=new RegExp(typeof e.position=="number"?`^.{${e.position}}${t}`:t);e.pattern=a,s._zod.onattach.push(i=>{const n=i._zod.bag;n.patterns??(n.patterns=new Set),n.patterns.add(a)}),s._zod.check=i=>{i.value.includes(e.includes,e.position)||i.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:e.includes,input:i.value,inst:s,continue:!e.abort})}}),bl=b("$ZodCheckStartsWith",(s,e)=>{me.init(s,e);const t=new RegExp(`^${qt(e.prefix)}.*`);e.pattern??(e.pattern=t),s._zod.onattach.push(a=>{const i=a._zod.bag;i.patterns??(i.patterns=new Set),i.patterns.add(t)}),s._zod.check=a=>{a.value.startsWith(e.prefix)||a.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:e.prefix,input:a.value,inst:s,continue:!e.abort})}}),wl=b("$ZodCheckEndsWith",(s,e)=>{me.init(s,e);const t=new RegExp(`.*${qt(e.suffix)}$`);e.pattern??(e.pattern=t),s._zod.onattach.push(a=>{const i=a._zod.bag;i.patterns??(i.patterns=new Set),i.patterns.add(t)}),s._zod.check=a=>{a.value.endsWith(e.suffix)||a.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:e.suffix,input:a.value,inst:s,continue:!e.abort})}}),Sl=b("$ZodCheckOverwrite",(s,e)=>{me.init(s,e),s._zod.check=t=>{t.value=e.tx(t.value)}});class _l{constructor(e=[]){this.content=[],this.indent=0,this&&(this.args=e)}indented(e){this.indent+=1,e(this),this.indent-=1}write(e){if(typeof e=="function"){e(this,{execution:"sync"}),e(this,{execution:"async"});return}const a=e.split(`
|
|
12
12
|
`).filter(r=>r),i=Math.min(...a.map(r=>r.length-r.trimStart().length)),n=a.map(r=>r.slice(i)).map(r=>" ".repeat(this.indent*2)+r);for(const r of n)this.content.push(r)}compile(){const e=Function,t=this?.args,i=[...(this?.content??[""]).map(n=>` ${n}`)];return new e(...t,i.join(`
|
|
13
|
-
`))}}const Al={major:4,minor:3,patch:6},Y=b("$ZodType",(s,e)=>{var t;s??(s={}),s._zod.def=e,s._zod.bag=s._zod.bag||{},s._zod.version=Al;const a=[...s._zod.def.checks??[]];s._zod.traits.has("$ZodCheck")&&a.unshift(s);for(const i of a)for(const n of i._zod.onattach)n(s);if(a.length===0)(t=s._zod).deferred??(t.deferred=[]),s._zod.deferred?.push(()=>{s._zod.run=s._zod.parse});else{const i=(r,o,l)=>{let c=je(r),u;for(const d of o){if(d._zod.def.when){if(!d._zod.def.when(r))continue}else if(c)continue;const h=r.issues.length,p=d._zod.check(r);if(p instanceof Promise&&l?.async===!1)throw new Ye;if(u||p instanceof Promise)u=(u??Promise.resolve()).then(async()=>{await p,r.issues.length!==h&&(c||(c=je(r,h)))});else{if(r.issues.length===h)continue;c||(c=je(r,h))}}return u?u.then(()=>r):r},n=(r,o,l)=>{if(je(r))return r.aborted=!0,r;const c=i(o,a,l);if(c instanceof Promise){if(l.async===!1)throw new Ye;return c.then(u=>s._zod.parse(u,l))}return s._zod.parse(c,l)};s._zod.run=(r,o)=>{if(o.skipChecks)return s._zod.parse(r,o);if(o.direction==="backward"){const c=s._zod.parse({value:r.value,issues:[]},{...o,skipChecks:!0});return c instanceof Promise?c.then(u=>n(u,r,o)):n(c,r,o)}const l=s._zod.parse(r,o);if(l instanceof Promise){if(o.async===!1)throw new Ye;return l.then(c=>i(c,a,o))}return i(l,a,o)}}
|
|
13
|
+
`))}}const Al={major:4,minor:3,patch:6},Y=b("$ZodType",(s,e)=>{var t;s??(s={}),s._zod.def=e,s._zod.bag=s._zod.bag||{},s._zod.version=Al;const a=[...s._zod.def.checks??[]];s._zod.traits.has("$ZodCheck")&&a.unshift(s);for(const i of a)for(const n of i._zod.onattach)n(s);if(a.length===0)(t=s._zod).deferred??(t.deferred=[]),s._zod.deferred?.push(()=>{s._zod.run=s._zod.parse});else{const i=(r,o,l)=>{let c=je(r),u;for(const d of o){if(d._zod.def.when){if(!d._zod.def.when(r))continue}else if(c)continue;const h=r.issues.length,p=d._zod.check(r);if(p instanceof Promise&&l?.async===!1)throw new Ye;if(u||p instanceof Promise)u=(u??Promise.resolve()).then(async()=>{await p,r.issues.length!==h&&(c||(c=je(r,h)))});else{if(r.issues.length===h)continue;c||(c=je(r,h))}}return u?u.then(()=>r):r},n=(r,o,l)=>{if(je(r))return r.aborted=!0,r;const c=i(o,a,l);if(c instanceof Promise){if(l.async===!1)throw new Ye;return c.then(u=>s._zod.parse(u,l))}return s._zod.parse(c,l)};s._zod.run=(r,o)=>{if(o.skipChecks)return s._zod.parse(r,o);if(o.direction==="backward"){const c=s._zod.parse({value:r.value,issues:[]},{...o,skipChecks:!0});return c instanceof Promise?c.then(u=>n(u,r,o)):n(c,r,o)}const l=s._zod.parse(r,o);if(l instanceof Promise){if(o.async===!1)throw new Ye;return l.then(c=>i(c,a,o))}return i(l,a,o)}}F(s,"~standard",()=>({validate:i=>{try{const n=Mo(s,i);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return Do(s,i).then(r=>r.success?{value:r.data}:{issues:r.error?.issues})}},vendor:"zod",version:1}))}),Fa=b("$ZodString",(s,e)=>{Y.init(s,e),s._zod.pattern=[...s?._zod.bag?.patterns??[]].pop()??nl(s._zod.bag),s._zod.parse=(t,a)=>{if(e.coerce)try{t.value=String(t.value)}catch{}return typeof t.value=="string"||t.issues.push({expected:"string",code:"invalid_type",input:t.value,inst:s}),t}}),G=b("$ZodStringFormat",(s,e)=>{jt.init(s,e),Fa.init(s,e)}),kl=b("$ZodGUID",(s,e)=>{e.pattern??(e.pattern=jo),G.init(s,e)}),Tl=b("$ZodUUID",(s,e)=>{if(e.version){const a={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[e.version];if(a===void 0)throw new Error(`Invalid UUID version: "${e.version}"`);e.pattern??(e.pattern=Li(a))}else e.pattern??(e.pattern=Li());G.init(s,e)}),Cl=b("$ZodEmail",(s,e)=>{e.pattern??(e.pattern=Vo),G.init(s,e)}),Il=b("$ZodURL",(s,e)=>{G.init(s,e),s._zod.check=t=>{try{const a=t.value.trim(),i=new URL(a);e.hostname&&(e.hostname.lastIndex=0,e.hostname.test(i.hostname)||t.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:e.hostname.source,input:t.value,inst:s,continue:!e.abort})),e.protocol&&(e.protocol.lastIndex=0,e.protocol.test(i.protocol.endsWith(":")?i.protocol.slice(0,-1):i.protocol)||t.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:e.protocol.source,input:t.value,inst:s,continue:!e.abort})),e.normalize?t.value=i.href:t.value=a;return}catch{t.issues.push({code:"invalid_format",format:"url",input:t.value,inst:s,continue:!e.abort})}}}),El=b("$ZodEmoji",(s,e)=>{e.pattern??(e.pattern=Zo()),G.init(s,e)}),Ml=b("$ZodNanoID",(s,e)=>{e.pattern??(e.pattern=Uo),G.init(s,e)}),Dl=b("$ZodCUID",(s,e)=>{e.pattern??(e.pattern=Fo),G.init(s,e)}),Rl=b("$ZodCUID2",(s,e)=>{e.pattern??(e.pattern=Ho),G.init(s,e)}),Pl=b("$ZodULID",(s,e)=>{e.pattern??(e.pattern=Wo),G.init(s,e)}),xl=b("$ZodXID",(s,e)=>{e.pattern??(e.pattern=zo),G.init(s,e)}),Nl=b("$ZodKSUID",(s,e)=>{e.pattern??(e.pattern=qo),G.init(s,e)}),$l=b("$ZodISODateTime",(s,e)=>{e.pattern??(e.pattern=sl(e)),G.init(s,e)}),Ll=b("$ZodISODate",(s,e)=>{e.pattern??(e.pattern=al),G.init(s,e)}),Ol=b("$ZodISOTime",(s,e)=>{e.pattern??(e.pattern=il(e)),G.init(s,e)}),Bl=b("$ZodISODuration",(s,e)=>{e.pattern??(e.pattern=Go),G.init(s,e)}),Fl=b("$ZodIPv4",(s,e)=>{e.pattern??(e.pattern=Xo),G.init(s,e),s._zod.bag.format="ipv4"}),Hl=b("$ZodIPv6",(s,e)=>{e.pattern??(e.pattern=Ko),G.init(s,e),s._zod.bag.format="ipv6",s._zod.check=t=>{try{new URL(`http://[${t.value}]`)}catch{t.issues.push({code:"invalid_format",format:"ipv6",input:t.value,inst:s,continue:!e.abort})}}}),Wl=b("$ZodCIDRv4",(s,e)=>{e.pattern??(e.pattern=Jo),G.init(s,e)}),zl=b("$ZodCIDRv6",(s,e)=>{e.pattern??(e.pattern=Qo),G.init(s,e),s._zod.check=t=>{const a=t.value.split("/");try{if(a.length!==2)throw new Error;const[i,n]=a;if(!n)throw new Error;const r=Number(n);if(`${r}`!==n)throw new Error;if(r<0||r>128)throw new Error;new URL(`http://[${i}]`)}catch{t.issues.push({code:"invalid_format",format:"cidrv6",input:t.value,inst:s,continue:!e.abort})}}});function an(s){if(s==="")return!0;if(s.length%4!==0)return!1;try{return atob(s),!0}catch{return!1}}const ql=b("$ZodBase64",(s,e)=>{e.pattern??(e.pattern=el),G.init(s,e),s._zod.bag.contentEncoding="base64",s._zod.check=t=>{an(t.value)||t.issues.push({code:"invalid_format",format:"base64",input:t.value,inst:s,continue:!e.abort})}});function Ul(s){if(!Zs.test(s))return!1;const e=s.replace(/[-_]/g,a=>a==="-"?"+":"/"),t=e.padEnd(Math.ceil(e.length/4)*4,"=");return an(t)}const Gl=b("$ZodBase64URL",(s,e)=>{e.pattern??(e.pattern=Zs),G.init(s,e),s._zod.bag.contentEncoding="base64url",s._zod.check=t=>{Ul(t.value)||t.issues.push({code:"invalid_format",format:"base64url",input:t.value,inst:s,continue:!e.abort})}}),jl=b("$ZodE164",(s,e)=>{e.pattern??(e.pattern=tl),G.init(s,e)});function Vl(s,e=null){try{const t=s.split(".");if(t.length!==3)return!1;const[a]=t;if(!a)return!1;const i=JSON.parse(atob(a));return!("typ"in i&&i?.typ!=="JWT"||!i.alg||e&&(!("alg"in i)||i.alg!==e))}catch{return!1}}const Yl=b("$ZodJWT",(s,e)=>{G.init(s,e),s._zod.check=t=>{Vl(t.value,e.alg)||t.issues.push({code:"invalid_format",format:"jwt",input:t.value,inst:s,continue:!e.abort})}}),sn=b("$ZodNumber",(s,e)=>{Y.init(s,e),s._zod.pattern=s._zod.bag.pattern??Js,s._zod.parse=(t,a)=>{if(e.coerce)try{t.value=Number(t.value)}catch{}const i=t.value;if(typeof i=="number"&&!Number.isNaN(i)&&Number.isFinite(i))return t;const n=typeof i=="number"?Number.isNaN(i)?"NaN":Number.isFinite(i)?void 0:"Infinity":void 0;return t.issues.push({expected:"number",code:"invalid_type",input:i,inst:s,...n?{received:n}:{}}),t}}),Zl=b("$ZodNumberFormat",(s,e)=>{dl.init(s,e),sn.init(s,e)}),Xl=b("$ZodBoolean",(s,e)=>{Y.init(s,e),s._zod.pattern=ol,s._zod.parse=(t,a)=>{if(e.coerce)try{t.value=!!t.value}catch{}const i=t.value;return typeof i=="boolean"||t.issues.push({expected:"boolean",code:"invalid_type",input:i,inst:s}),t}}),Kl=b("$ZodUnknown",(s,e)=>{Y.init(s,e),s._zod.parse=t=>t}),Jl=b("$ZodNever",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>(t.issues.push({expected:"never",code:"invalid_type",input:t.value,inst:s}),t)});function Oi(s,e,t){s.issues.length&&e.issues.push(...Ve(t,s.issues)),e.value[t]=s.value}const Ql=b("$ZodArray",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{const i=t.value;if(!Array.isArray(i))return t.issues.push({expected:"array",code:"invalid_type",input:i,inst:s}),t;t.value=Array(i.length);const n=[];for(let r=0;r<i.length;r++){const o=i[r],l=e.element._zod.run({value:o,issues:[]},a);l instanceof Promise?n.push(l.then(c=>Oi(c,t,r))):Oi(l,t,r)}return n.length?Promise.all(n).then(()=>t):t}});function Nt(s,e,t,a,i){if(s.issues.length){if(i&&!(t in a))return;e.issues.push(...Ve(t,s.issues))}s.value===void 0?t in a&&(e.value[t]=void 0):e.value[t]=s.value}function nn(s){const e=Object.keys(s.shape);for(const a of e)if(!s.shape?.[a]?._zod?.traits?.has("$ZodType"))throw new Error(`Invalid element at key "${a}": expected a Zod schema`);const t=vo(s.shape);return{...s,keys:e,keySet:new Set(e),numKeys:e.length,optionalKeys:new Set(t)}}function rn(s,e,t,a,i,n){const r=[],o=i.keySet,l=i.catchall._zod,c=l.def.type,u=l.optout==="optional";for(const d in e){if(o.has(d))continue;if(c==="never"){r.push(d);continue}const h=l.run({value:e[d],issues:[]},a);h instanceof Promise?s.push(h.then(p=>Nt(p,t,d,e,u))):Nt(h,t,d,e,u)}return r.length&&t.issues.push({code:"unrecognized_keys",keys:r,input:e,inst:n}),s.length?Promise.all(s).then(()=>t):t}const ec=b("$ZodObject",(s,e)=>{if(Y.init(s,e),!Object.getOwnPropertyDescriptor(e,"shape")?.get){const o=e.shape;Object.defineProperty(e,"shape",{get:()=>{const l={...o};return Object.defineProperty(e,"shape",{value:l}),l}})}const a=xa(()=>nn(e));F(s._zod,"propValues",()=>{const o=e.shape,l={};for(const c in o){const u=o[c]._zod;if(u.values){l[c]??(l[c]=new Set);for(const d of u.values)l[c].add(d)}}return l});const i=xt,n=e.catchall;let r;s._zod.parse=(o,l)=>{r??(r=a.value);const c=o.value;if(!i(c))return o.issues.push({expected:"object",code:"invalid_type",input:c,inst:s}),o;o.value={};const u=[],d=r.shape;for(const h of r.keys){const p=d[h],g=p._zod.optout==="optional",m=p._zod.run({value:c[h],issues:[]},l);m instanceof Promise?u.push(m.then(f=>Nt(f,o,h,c,g))):Nt(m,o,h,c,g)}return n?rn(u,c,o,l,a.value,s):u.length?Promise.all(u).then(()=>o):o}}),tc=b("$ZodObjectJIT",(s,e)=>{ec.init(s,e);const t=s._zod.parse,a=xa(()=>nn(e)),i=h=>{const p=new _l(["shape","payload","ctx"]),g=a.value,m=E=>{const I=$i(E);return`shape[${I}]._zod.run({ value: input[${I}], issues: [] }, ctx)`};p.write("const input = payload.value;");const f=Object.create(null);let v=0;for(const E of g.keys)f[E]=`key_${v++}`;p.write("const newResult = {};");for(const E of g.keys){const I=f[E],D=$i(E),N=h[E]?._zod?.optout==="optional";p.write(`const ${I} = ${m(E)};`),N?p.write(`
|
|
14
14
|
if (${I}.issues.length) {
|
|
15
15
|
if (${D} in input) {
|
|
16
16
|
payload.issues = payload.issues.concat(${I}.issues.map(iss => ({
|
|
@@ -44,9 +44,9 @@ ${n.map(d=>` - ${d}`).join(`
|
|
|
44
44
|
newResult[${D}] = ${I}.value;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
`)}p.write("payload.value = newResult;"),p.write("return payload;");const w=p.compile();return(E,I)=>w(h,E,I)};let n;const r=xt,o=!zs.jitless,c=o&&fo.value,u=e.catchall;let d;s._zod.parse=(h,p)=>{d??(d=a.value);const g=h.value;return r(g)?o&&c&&p?.async===!1&&p.jitless!==!0?(n||(n=i(e.shape)),h=n(h,p),u?rn([],g,h,p,d,s):h):t(h,p):(h.issues.push({expected:"object",code:"invalid_type",input:g,inst:s}),h)}});function Bi(s,e,t,a){for(const n of s)if(n.issues.length===0)return e.value=n.value,e;const i=s.filter(n=>!je(n));return i.length===1?(e.value=i[0].value,i[0]):(e.issues.push({code:"invalid_union",input:e.value,inst:t,errors:s.map(n=>n.issues.map(r=>Ne(r,a,xe())))}),e)}const ac=b("$ZodUnion",(s,e)=>{Y.init(s,e),B(s._zod,"optin",()=>e.options.some(i=>i._zod.optin==="optional")?"optional":void 0),B(s._zod,"optout",()=>e.options.some(i=>i._zod.optout==="optional")?"optional":void 0),B(s._zod,"values",()=>{if(e.options.every(i=>i._zod.values))return new Set(e.options.flatMap(i=>Array.from(i._zod.values)))}),B(s._zod,"pattern",()=>{if(e.options.every(i=>i._zod.pattern)){const i=e.options.map(n=>n._zod.pattern);return new RegExp(`^(${i.map(n=>$a(n.source)).join("|")})$`)}});const t=e.options.length===1,a=e.options[0]._zod.run;s._zod.parse=(i,n)=>{if(t)return a(i,n);let r=!1;const o=[];for(const l of e.options){const c=l._zod.run({value:i.value,issues:[]},n);if(c instanceof Promise)o.push(c),r=!0;else{if(c.issues.length===0)return c;o.push(c)}}return r?Promise.all(o).then(l=>Bi(l,i,s,n)):Bi(o,i,s,n)}}),ic=b("$ZodIntersection",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{const i=t.value,n=e.left._zod.run({value:i,issues:[]},a),r=e.right._zod.run({value:i,issues:[]},a);return n instanceof Promise||r instanceof Promise?Promise.all([n,r]).then(([l,c])=>Fi(t,l,c)):Fi(t,n,r)}});function da(s,e){if(s===e)return{valid:!0,data:s};if(s instanceof Date&&e instanceof Date&&+s==+e)return{valid:!0,data:s};if(Je(s)&&Je(e)){const t=Object.keys(e),a=Object.keys(s).filter(n=>t.indexOf(n)!==-1),i={...s,...e};for(const n of a){const r=da(s[n],e[n]);if(!r.valid)return{valid:!1,mergeErrorPath:[n,...r.mergeErrorPath]};i[n]=r.data}return{valid:!0,data:i}}if(Array.isArray(s)&&Array.isArray(e)){if(s.length!==e.length)return{valid:!1,mergeErrorPath:[]};const t=[];for(let a=0;a<s.length;a++){const i=s[a],n=e[a],r=da(i,n);if(!r.valid)return{valid:!1,mergeErrorPath:[a,...r.mergeErrorPath]};t.push(r.data)}return{valid:!0,data:t}}return{valid:!1,mergeErrorPath:[]}}function Fi(s,e,t){const a=new Map;let i;for(const o of e.issues)if(o.code==="unrecognized_keys"){i??(i=o);for(const l of o.keys)a.has(l)||a.set(l,{}),a.get(l).l=!0}else s.issues.push(o);for(const o of t.issues)if(o.code==="unrecognized_keys")for(const l of o.keys)a.has(l)||a.set(l,{}),a.get(l).r=!0;else s.issues.push(o);const n=[...a].filter(([,o])=>o.l&&o.r).map(([o])=>o);if(n.length&&i&&s.issues.push({...i,keys:n}),je(s))return s;const r=da(e.value,t.value);if(!r.valid)throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(r.mergeErrorPath)}`);return s.value=r.data,s}const sc=b("$ZodRecord",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{const i=t.value;if(!Je(i))return t.issues.push({expected:"record",code:"invalid_type",input:i,inst:s}),t;const n=[],r=e.keyType._zod.values;if(r){t.value={};const o=new Set;for(const c of r)if(typeof c=="string"||typeof c=="number"||typeof c=="symbol"){o.add(typeof c=="number"?c.toString():c);const u=e.valueType._zod.run({value:i[c],issues:[]},a);u instanceof Promise?n.push(u.then(d=>{d.issues.length&&t.issues.push(...Ve(c,d.issues)),t.value[c]=d.value})):(u.issues.length&&t.issues.push(...Ve(c,u.issues)),t.value[c]=u.value)}let l;for(const c in i)o.has(c)||(l=l??[],l.push(c));l&&l.length>0&&t.issues.push({code:"unrecognized_keys",input:i,inst:s,keys:l})}else{t.value={};for(const o of Reflect.ownKeys(i)){if(o==="__proto__")continue;let l=e.keyType._zod.run({value:o,issues:[]},a);if(l instanceof Promise)throw new Error("Async schemas not supported in object keys currently");if(typeof o=="string"&&Js.test(o)&&l.issues.length){const d=e.keyType._zod.run({value:Number(o),issues:[]},a);if(d instanceof Promise)throw new Error("Async schemas not supported in object keys currently");d.issues.length===0&&(l=d)}if(l.issues.length){e.mode==="loose"?t.value[o]=i[o]:t.issues.push({code:"invalid_key",origin:"record",issues:l.issues.map(d=>Ne(d,a,xe())),input:o,path:[o],inst:s});continue}const u=e.valueType._zod.run({value:i[o],issues:[]},a);u instanceof Promise?n.push(u.then(d=>{d.issues.length&&t.issues.push(...Ve(o,d.issues)),t.value[l.value]=d.value})):(u.issues.length&&t.issues.push(...Ve(o,u.issues)),t.value[l.value]=u.value)}}return n.length?Promise.all(n).then(()=>t):t}}),nc=b("$ZodEnum",(s,e)=>{Y.init(s,e);const t=qs(e.entries),a=new Set(t);s._zod.values=a,s._zod.pattern=new RegExp(`^(${t.filter(i=>yo.has(typeof i)).map(i=>typeof i=="string"?qt(i):i.toString()).join("|")})$`),s._zod.parse=(i,n)=>{const r=i.value;return a.has(r)||i.issues.push({code:"invalid_value",values:t,input:r,inst:s}),i}}),rc=b("$ZodTransform",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{if(a.direction==="backward")throw new Ws(s.constructor.name);const i=e.transform(t.value,t);if(a.async)return(i instanceof Promise?i:Promise.resolve(i)).then(r=>(t.value=r,t));if(i instanceof Promise)throw new Ye;return t.value=i,t}});function Hi(s,e){return s.issues.length&&e===void 0?{issues:[],value:void 0}:s}const on=b("$ZodOptional",(s,e)=>{Y.init(s,e),s._zod.optin="optional",s._zod.optout="optional",B(s._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,void 0]):void 0),B(s._zod,"pattern",()=>{const t=e.innerType._zod.pattern;return t?new RegExp(`^(${$a(t.source)})?$`):void 0}),s._zod.parse=(t,a)=>{if(e.innerType._zod.optin==="optional"){const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>Hi(n,t.value)):Hi(i,t.value)}return t.value===void 0?t:e.innerType._zod.run(t,a)}}),oc=b("$ZodExactOptional",(s,e)=>{on.init(s,e),B(s._zod,"values",()=>e.innerType._zod.values),B(s._zod,"pattern",()=>e.innerType._zod.pattern),s._zod.parse=(t,a)=>e.innerType._zod.run(t,a)}),lc=b("$ZodNullable",(s,e)=>{Y.init(s,e),B(s._zod,"optin",()=>e.innerType._zod.optin),B(s._zod,"optout",()=>e.innerType._zod.optout),B(s._zod,"pattern",()=>{const t=e.innerType._zod.pattern;return t?new RegExp(`^(${$a(t.source)}|null)$`):void 0}),B(s._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,null]):void 0),s._zod.parse=(t,a)=>t.value===null?t:e.innerType._zod.run(t,a)}),cc=b("$ZodDefault",(s,e)=>{Y.init(s,e),s._zod.optin="optional",B(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);if(t.value===void 0)return t.value=e.defaultValue,t;const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>Wi(n,e)):Wi(i,e)}});function Wi(s,e){return s.value===void 0&&(s.value=e.defaultValue),s}const uc=b("$ZodPrefault",(s,e)=>{Y.init(s,e),s._zod.optin="optional",B(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>(a.direction==="backward"||t.value===void 0&&(t.value=e.defaultValue),e.innerType._zod.run(t,a))}),dc=b("$ZodNonOptional",(s,e)=>{Y.init(s,e),B(s._zod,"values",()=>{const t=e.innerType._zod.values;return t?new Set([...t].filter(a=>a!==void 0)):void 0}),s._zod.parse=(t,a)=>{const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>zi(n,s)):zi(i,s)}});function zi(s,e){return!s.issues.length&&s.value===void 0&&s.issues.push({code:"invalid_type",expected:"nonoptional",input:s.value,inst:e}),s}const hc=b("$ZodCatch",(s,e)=>{Y.init(s,e),B(s._zod,"optin",()=>e.innerType._zod.optin),B(s._zod,"optout",()=>e.innerType._zod.optout),B(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>(t.value=n.value,n.issues.length&&(t.value=e.catchValue({...t,error:{issues:n.issues.map(r=>Ne(r,a,xe()))},input:t.value}),t.issues=[]),t)):(t.value=i.value,i.issues.length&&(t.value=e.catchValue({...t,error:{issues:i.issues.map(n=>Ne(n,a,xe()))},input:t.value}),t.issues=[]),t)}}),pc=b("$ZodPipe",(s,e)=>{Y.init(s,e),B(s._zod,"values",()=>e.in._zod.values),B(s._zod,"optin",()=>e.in._zod.optin),B(s._zod,"optout",()=>e.out._zod.optout),B(s._zod,"propValues",()=>e.in._zod.propValues),s._zod.parse=(t,a)=>{if(a.direction==="backward"){const n=e.out._zod.run(t,a);return n instanceof Promise?n.then(r=>kt(r,e.in,a)):kt(n,e.in,a)}const i=e.in._zod.run(t,a);return i instanceof Promise?i.then(n=>kt(n,e.out,a)):kt(i,e.out,a)}});function kt(s,e,t){return s.issues.length?(s.aborted=!0,s):e._zod.run({value:s.value,issues:s.issues},t)}const mc=b("$ZodReadonly",(s,e)=>{Y.init(s,e),B(s._zod,"propValues",()=>e.innerType._zod.propValues),B(s._zod,"values",()=>e.innerType._zod.values),B(s._zod,"optin",()=>e.innerType?._zod?.optin),B(s._zod,"optout",()=>e.innerType?._zod?.optout),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(qi):qi(i)}});function qi(s){return s.value=Object.freeze(s.value),s}const gc=b("$ZodCustom",(s,e)=>{me.init(s,e),Y.init(s,e),s._zod.parse=(t,a)=>t,s._zod.check=t=>{const a=t.value,i=e.fn(a);if(i instanceof Promise)return i.then(n=>Ui(n,t,a,s));Ui(i,t,a,s)}});function Ui(s,e,t,a){if(!s){const i={code:"custom",input:t,inst:a,path:[...a._zod.def.path??[]],continue:!a._zod.def.abort};a._zod.def.params&&(i.params=a._zod.def.params),e.issues.push(ut(i))}}var Gi;class fc{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){const a=t[0];return this._map.set(e,a),a&&typeof a=="object"&&"id"in a&&this._idmap.set(a.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){const t=this._map.get(e);return t&&typeof t=="object"&&"id"in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){const t=e._zod.parent;if(t){const a={...this.get(t)??{}};delete a.id;const i={...a,...this._map.get(e)};return Object.keys(i).length?i:void 0}return this._map.get(e)}has(e){return this._map.has(e)}}function yc(){return new fc}(Gi=globalThis).__zod_globalRegistry??(Gi.__zod_globalRegistry=yc());const nt=globalThis.__zod_globalRegistry;function vc(s,e){return new s({type:"string",...M(e)})}function bc(s,e){return new s({type:"string",format:"email",check:"string_format",abort:!1,...M(e)})}function ji(s,e){return new s({type:"string",format:"guid",check:"string_format",abort:!1,...M(e)})}function wc(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,...M(e)})}function Sc(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v4",...M(e)})}function _c(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v6",...M(e)})}function Ac(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v7",...M(e)})}function kc(s,e){return new s({type:"string",format:"url",check:"string_format",abort:!1,...M(e)})}function Tc(s,e){return new s({type:"string",format:"emoji",check:"string_format",abort:!1,...M(e)})}function Cc(s,e){return new s({type:"string",format:"nanoid",check:"string_format",abort:!1,...M(e)})}function Ic(s,e){return new s({type:"string",format:"cuid",check:"string_format",abort:!1,...M(e)})}function Ec(s,e){return new s({type:"string",format:"cuid2",check:"string_format",abort:!1,...M(e)})}function Mc(s,e){return new s({type:"string",format:"ulid",check:"string_format",abort:!1,...M(e)})}function Dc(s,e){return new s({type:"string",format:"xid",check:"string_format",abort:!1,...M(e)})}function Rc(s,e){return new s({type:"string",format:"ksuid",check:"string_format",abort:!1,...M(e)})}function Pc(s,e){return new s({type:"string",format:"ipv4",check:"string_format",abort:!1,...M(e)})}function xc(s,e){return new s({type:"string",format:"ipv6",check:"string_format",abort:!1,...M(e)})}function Nc(s,e){return new s({type:"string",format:"cidrv4",check:"string_format",abort:!1,...M(e)})}function $c(s,e){return new s({type:"string",format:"cidrv6",check:"string_format",abort:!1,...M(e)})}function Lc(s,e){return new s({type:"string",format:"base64",check:"string_format",abort:!1,...M(e)})}function Oc(s,e){return new s({type:"string",format:"base64url",check:"string_format",abort:!1,...M(e)})}function Bc(s,e){return new s({type:"string",format:"e164",check:"string_format",abort:!1,...M(e)})}function Fc(s,e){return new s({type:"string",format:"jwt",check:"string_format",abort:!1,...M(e)})}function Hc(s,e){return new s({type:"string",format:"datetime",check:"string_format",offset:!1,local:!1,precision:null,...M(e)})}function Wc(s,e){return new s({type:"string",format:"date",check:"string_format",...M(e)})}function zc(s,e){return new s({type:"string",format:"time",check:"string_format",precision:null,...M(e)})}function qc(s,e){return new s({type:"string",format:"duration",check:"string_format",...M(e)})}function Uc(s,e){return new s({type:"number",checks:[],...M(e)})}function Gc(s,e){return new s({type:"number",check:"number_format",abort:!1,format:"safeint",...M(e)})}function jc(s,e){return new s({type:"boolean",...M(e)})}function Vc(s){return new s({type:"unknown"})}function Yc(s,e){return new s({type:"never",...M(e)})}function Vi(s,e){return new en({check:"less_than",...M(e),value:s,inclusive:!1})}function Jt(s,e){return new en({check:"less_than",...M(e),value:s,inclusive:!0})}function Yi(s,e){return new tn({check:"greater_than",...M(e),value:s,inclusive:!1})}function Qt(s,e){return new tn({check:"greater_than",...M(e),value:s,inclusive:!0})}function Zi(s,e){return new ul({check:"multiple_of",...M(e),value:s})}function ln(s,e){return new hl({check:"max_length",...M(e),maximum:s})}function $t(s,e){return new pl({check:"min_length",...M(e),minimum:s})}function cn(s,e){return new ml({check:"length_equals",...M(e),length:s})}function Zc(s,e){return new gl({check:"string_format",format:"regex",...M(e),pattern:s})}function Xc(s){return new fl({check:"string_format",format:"lowercase",...M(s)})}function Kc(s){return new yl({check:"string_format",format:"uppercase",...M(s)})}function Jc(s,e){return new vl({check:"string_format",format:"includes",...M(e),includes:s})}function Qc(s,e){return new bl({check:"string_format",format:"starts_with",...M(e),prefix:s})}function eu(s,e){return new wl({check:"string_format",format:"ends_with",...M(e),suffix:s})}function et(s){return new Sl({check:"overwrite",tx:s})}function tu(s){return et(e=>e.normalize(s))}function au(){return et(s=>s.trim())}function iu(){return et(s=>s.toLowerCase())}function su(){return et(s=>s.toUpperCase())}function nu(){return et(s=>go(s))}function ru(s,e,t){return new s({type:"array",element:e,...M(t)})}function ou(s,e,t){return new s({type:"custom",check:"custom",fn:e,...M(t)})}function lu(s){const e=cu(t=>(t.addIssue=a=>{if(typeof a=="string")t.issues.push(ut(a,t.value,e._zod.def));else{const i=a;i.fatal&&(i.continue=!1),i.code??(i.code="custom"),i.input??(i.input=t.value),i.inst??(i.inst=e),i.continue??(i.continue=!e._zod.def.abort),t.issues.push(ut(i))}},s(t.value,t)));return e}function cu(s,e){const t=new me({check:"custom",...M(e)});return t._zod.check=s,t}function un(s){let e=s?.target??"draft-2020-12";return e==="draft-4"&&(e="draft-04"),e==="draft-7"&&(e="draft-07"),{processors:s.processors??{},metadataRegistry:s?.metadata??nt,target:e,unrepresentable:s?.unrepresentable??"throw",override:s?.override??(()=>{}),io:s?.io??"output",counter:0,seen:new Map,cycles:s?.cycles??"ref",reused:s?.reused??"inline",external:s?.external??void 0}}function K(s,e,t={path:[],schemaPath:[]}){var a;const i=s._zod.def,n=e.seen.get(s);if(n)return n.count++,t.schemaPath.includes(s)&&(n.cycle=t.path),n.schema;const r={schema:{},count:1,cycle:void 0,path:t.path};e.seen.set(s,r);const o=s._zod.toJSONSchema?.();if(o)r.schema=o;else{const u={...t,schemaPath:[...t.schemaPath,s],path:t.path};if(s._zod.processJSONSchema)s._zod.processJSONSchema(e,r.schema,u);else{const h=r.schema,p=e.processors[i.type];if(!p)throw new Error(`[toJSONSchema]: Non-representable type encountered: ${i.type}`);p(s,e,h,u)}const d=s._zod.parent;d&&(r.ref||(r.ref=d),K(d,e,u),e.seen.get(d).isParent=!0)}const l=e.metadataRegistry.get(s);return l&&Object.assign(r.schema,l),e.io==="input"&&ce(s)&&(delete r.schema.examples,delete r.schema.default),e.io==="input"&&r.schema._prefault&&((a=r.schema).default??(a.default=r.schema._prefault)),delete r.schema._prefault,e.seen.get(s).schema}function dn(s,e){const t=s.seen.get(e);if(!t)throw new Error("Unprocessed schema. This is a bug in Zod.");const a=new Map;for(const r of s.seen.entries()){const o=s.metadataRegistry.get(r[0])?.id;if(o){const l=a.get(o);if(l&&l!==r[0])throw new Error(`Duplicate schema id "${o}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);a.set(o,r[0])}}const i=r=>{const o=s.target==="draft-2020-12"?"$defs":"definitions";if(s.external){const d=s.external.registry.get(r[0])?.id,h=s.external.uri??(g=>g);if(d)return{ref:h(d)};const p=r[1].defId??r[1].schema.id??`schema${s.counter++}`;return r[1].defId=p,{defId:p,ref:`${h("__shared")}#/${o}/${p}`}}if(r[1]===t)return{ref:"#"};const c=`#/${o}/`,u=r[1].schema.id??`__schema${s.counter++}`;return{defId:u,ref:c+u}},n=r=>{if(r[1].schema.$ref)return;const o=r[1],{ref:l,defId:c}=i(r);o.def={...o.schema},c&&(o.defId=c);const u=o.schema;for(const d in u)delete u[d];u.$ref=l};if(s.cycles==="throw")for(const r of s.seen.entries()){const o=r[1];if(o.cycle)throw new Error(`Cycle detected: #/${o.cycle?.join("/")}/<root>
|
|
47
|
+
`)}p.write("payload.value = newResult;"),p.write("return payload;");const w=p.compile();return(E,I)=>w(h,E,I)};let n;const r=xt,o=!zs.jitless,c=o&&fo.value,u=e.catchall;let d;s._zod.parse=(h,p)=>{d??(d=a.value);const g=h.value;return r(g)?o&&c&&p?.async===!1&&p.jitless!==!0?(n||(n=i(e.shape)),h=n(h,p),u?rn([],g,h,p,d,s):h):t(h,p):(h.issues.push({expected:"object",code:"invalid_type",input:g,inst:s}),h)}});function Bi(s,e,t,a){for(const n of s)if(n.issues.length===0)return e.value=n.value,e;const i=s.filter(n=>!je(n));return i.length===1?(e.value=i[0].value,i[0]):(e.issues.push({code:"invalid_union",input:e.value,inst:t,errors:s.map(n=>n.issues.map(r=>Ne(r,a,xe())))}),e)}const ac=b("$ZodUnion",(s,e)=>{Y.init(s,e),F(s._zod,"optin",()=>e.options.some(i=>i._zod.optin==="optional")?"optional":void 0),F(s._zod,"optout",()=>e.options.some(i=>i._zod.optout==="optional")?"optional":void 0),F(s._zod,"values",()=>{if(e.options.every(i=>i._zod.values))return new Set(e.options.flatMap(i=>Array.from(i._zod.values)))}),F(s._zod,"pattern",()=>{if(e.options.every(i=>i._zod.pattern)){const i=e.options.map(n=>n._zod.pattern);return new RegExp(`^(${i.map(n=>$a(n.source)).join("|")})$`)}});const t=e.options.length===1,a=e.options[0]._zod.run;s._zod.parse=(i,n)=>{if(t)return a(i,n);let r=!1;const o=[];for(const l of e.options){const c=l._zod.run({value:i.value,issues:[]},n);if(c instanceof Promise)o.push(c),r=!0;else{if(c.issues.length===0)return c;o.push(c)}}return r?Promise.all(o).then(l=>Bi(l,i,s,n)):Bi(o,i,s,n)}}),ic=b("$ZodIntersection",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{const i=t.value,n=e.left._zod.run({value:i,issues:[]},a),r=e.right._zod.run({value:i,issues:[]},a);return n instanceof Promise||r instanceof Promise?Promise.all([n,r]).then(([l,c])=>Fi(t,l,c)):Fi(t,n,r)}});function da(s,e){if(s===e)return{valid:!0,data:s};if(s instanceof Date&&e instanceof Date&&+s==+e)return{valid:!0,data:s};if(Je(s)&&Je(e)){const t=Object.keys(e),a=Object.keys(s).filter(n=>t.indexOf(n)!==-1),i={...s,...e};for(const n of a){const r=da(s[n],e[n]);if(!r.valid)return{valid:!1,mergeErrorPath:[n,...r.mergeErrorPath]};i[n]=r.data}return{valid:!0,data:i}}if(Array.isArray(s)&&Array.isArray(e)){if(s.length!==e.length)return{valid:!1,mergeErrorPath:[]};const t=[];for(let a=0;a<s.length;a++){const i=s[a],n=e[a],r=da(i,n);if(!r.valid)return{valid:!1,mergeErrorPath:[a,...r.mergeErrorPath]};t.push(r.data)}return{valid:!0,data:t}}return{valid:!1,mergeErrorPath:[]}}function Fi(s,e,t){const a=new Map;let i;for(const o of e.issues)if(o.code==="unrecognized_keys"){i??(i=o);for(const l of o.keys)a.has(l)||a.set(l,{}),a.get(l).l=!0}else s.issues.push(o);for(const o of t.issues)if(o.code==="unrecognized_keys")for(const l of o.keys)a.has(l)||a.set(l,{}),a.get(l).r=!0;else s.issues.push(o);const n=[...a].filter(([,o])=>o.l&&o.r).map(([o])=>o);if(n.length&&i&&s.issues.push({...i,keys:n}),je(s))return s;const r=da(e.value,t.value);if(!r.valid)throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(r.mergeErrorPath)}`);return s.value=r.data,s}const sc=b("$ZodRecord",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{const i=t.value;if(!Je(i))return t.issues.push({expected:"record",code:"invalid_type",input:i,inst:s}),t;const n=[],r=e.keyType._zod.values;if(r){t.value={};const o=new Set;for(const c of r)if(typeof c=="string"||typeof c=="number"||typeof c=="symbol"){o.add(typeof c=="number"?c.toString():c);const u=e.valueType._zod.run({value:i[c],issues:[]},a);u instanceof Promise?n.push(u.then(d=>{d.issues.length&&t.issues.push(...Ve(c,d.issues)),t.value[c]=d.value})):(u.issues.length&&t.issues.push(...Ve(c,u.issues)),t.value[c]=u.value)}let l;for(const c in i)o.has(c)||(l=l??[],l.push(c));l&&l.length>0&&t.issues.push({code:"unrecognized_keys",input:i,inst:s,keys:l})}else{t.value={};for(const o of Reflect.ownKeys(i)){if(o==="__proto__")continue;let l=e.keyType._zod.run({value:o,issues:[]},a);if(l instanceof Promise)throw new Error("Async schemas not supported in object keys currently");if(typeof o=="string"&&Js.test(o)&&l.issues.length){const d=e.keyType._zod.run({value:Number(o),issues:[]},a);if(d instanceof Promise)throw new Error("Async schemas not supported in object keys currently");d.issues.length===0&&(l=d)}if(l.issues.length){e.mode==="loose"?t.value[o]=i[o]:t.issues.push({code:"invalid_key",origin:"record",issues:l.issues.map(d=>Ne(d,a,xe())),input:o,path:[o],inst:s});continue}const u=e.valueType._zod.run({value:i[o],issues:[]},a);u instanceof Promise?n.push(u.then(d=>{d.issues.length&&t.issues.push(...Ve(o,d.issues)),t.value[l.value]=d.value})):(u.issues.length&&t.issues.push(...Ve(o,u.issues)),t.value[l.value]=u.value)}}return n.length?Promise.all(n).then(()=>t):t}}),nc=b("$ZodEnum",(s,e)=>{Y.init(s,e);const t=qs(e.entries),a=new Set(t);s._zod.values=a,s._zod.pattern=new RegExp(`^(${t.filter(i=>yo.has(typeof i)).map(i=>typeof i=="string"?qt(i):i.toString()).join("|")})$`),s._zod.parse=(i,n)=>{const r=i.value;return a.has(r)||i.issues.push({code:"invalid_value",values:t,input:r,inst:s}),i}}),rc=b("$ZodTransform",(s,e)=>{Y.init(s,e),s._zod.parse=(t,a)=>{if(a.direction==="backward")throw new Ws(s.constructor.name);const i=e.transform(t.value,t);if(a.async)return(i instanceof Promise?i:Promise.resolve(i)).then(r=>(t.value=r,t));if(i instanceof Promise)throw new Ye;return t.value=i,t}});function Hi(s,e){return s.issues.length&&e===void 0?{issues:[],value:void 0}:s}const on=b("$ZodOptional",(s,e)=>{Y.init(s,e),s._zod.optin="optional",s._zod.optout="optional",F(s._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,void 0]):void 0),F(s._zod,"pattern",()=>{const t=e.innerType._zod.pattern;return t?new RegExp(`^(${$a(t.source)})?$`):void 0}),s._zod.parse=(t,a)=>{if(e.innerType._zod.optin==="optional"){const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>Hi(n,t.value)):Hi(i,t.value)}return t.value===void 0?t:e.innerType._zod.run(t,a)}}),oc=b("$ZodExactOptional",(s,e)=>{on.init(s,e),F(s._zod,"values",()=>e.innerType._zod.values),F(s._zod,"pattern",()=>e.innerType._zod.pattern),s._zod.parse=(t,a)=>e.innerType._zod.run(t,a)}),lc=b("$ZodNullable",(s,e)=>{Y.init(s,e),F(s._zod,"optin",()=>e.innerType._zod.optin),F(s._zod,"optout",()=>e.innerType._zod.optout),F(s._zod,"pattern",()=>{const t=e.innerType._zod.pattern;return t?new RegExp(`^(${$a(t.source)}|null)$`):void 0}),F(s._zod,"values",()=>e.innerType._zod.values?new Set([...e.innerType._zod.values,null]):void 0),s._zod.parse=(t,a)=>t.value===null?t:e.innerType._zod.run(t,a)}),cc=b("$ZodDefault",(s,e)=>{Y.init(s,e),s._zod.optin="optional",F(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);if(t.value===void 0)return t.value=e.defaultValue,t;const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>Wi(n,e)):Wi(i,e)}});function Wi(s,e){return s.value===void 0&&(s.value=e.defaultValue),s}const uc=b("$ZodPrefault",(s,e)=>{Y.init(s,e),s._zod.optin="optional",F(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>(a.direction==="backward"||t.value===void 0&&(t.value=e.defaultValue),e.innerType._zod.run(t,a))}),dc=b("$ZodNonOptional",(s,e)=>{Y.init(s,e),F(s._zod,"values",()=>{const t=e.innerType._zod.values;return t?new Set([...t].filter(a=>a!==void 0)):void 0}),s._zod.parse=(t,a)=>{const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>zi(n,s)):zi(i,s)}});function zi(s,e){return!s.issues.length&&s.value===void 0&&s.issues.push({code:"invalid_type",expected:"nonoptional",input:s.value,inst:e}),s}const hc=b("$ZodCatch",(s,e)=>{Y.init(s,e),F(s._zod,"optin",()=>e.innerType._zod.optin),F(s._zod,"optout",()=>e.innerType._zod.optout),F(s._zod,"values",()=>e.innerType._zod.values),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(n=>(t.value=n.value,n.issues.length&&(t.value=e.catchValue({...t,error:{issues:n.issues.map(r=>Ne(r,a,xe()))},input:t.value}),t.issues=[]),t)):(t.value=i.value,i.issues.length&&(t.value=e.catchValue({...t,error:{issues:i.issues.map(n=>Ne(n,a,xe()))},input:t.value}),t.issues=[]),t)}}),pc=b("$ZodPipe",(s,e)=>{Y.init(s,e),F(s._zod,"values",()=>e.in._zod.values),F(s._zod,"optin",()=>e.in._zod.optin),F(s._zod,"optout",()=>e.out._zod.optout),F(s._zod,"propValues",()=>e.in._zod.propValues),s._zod.parse=(t,a)=>{if(a.direction==="backward"){const n=e.out._zod.run(t,a);return n instanceof Promise?n.then(r=>kt(r,e.in,a)):kt(n,e.in,a)}const i=e.in._zod.run(t,a);return i instanceof Promise?i.then(n=>kt(n,e.out,a)):kt(i,e.out,a)}});function kt(s,e,t){return s.issues.length?(s.aborted=!0,s):e._zod.run({value:s.value,issues:s.issues},t)}const mc=b("$ZodReadonly",(s,e)=>{Y.init(s,e),F(s._zod,"propValues",()=>e.innerType._zod.propValues),F(s._zod,"values",()=>e.innerType._zod.values),F(s._zod,"optin",()=>e.innerType?._zod?.optin),F(s._zod,"optout",()=>e.innerType?._zod?.optout),s._zod.parse=(t,a)=>{if(a.direction==="backward")return e.innerType._zod.run(t,a);const i=e.innerType._zod.run(t,a);return i instanceof Promise?i.then(qi):qi(i)}});function qi(s){return s.value=Object.freeze(s.value),s}const gc=b("$ZodCustom",(s,e)=>{me.init(s,e),Y.init(s,e),s._zod.parse=(t,a)=>t,s._zod.check=t=>{const a=t.value,i=e.fn(a);if(i instanceof Promise)return i.then(n=>Ui(n,t,a,s));Ui(i,t,a,s)}});function Ui(s,e,t,a){if(!s){const i={code:"custom",input:t,inst:a,path:[...a._zod.def.path??[]],continue:!a._zod.def.abort};a._zod.def.params&&(i.params=a._zod.def.params),e.issues.push(ut(i))}}var Gi;class fc{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){const a=t[0];return this._map.set(e,a),a&&typeof a=="object"&&"id"in a&&this._idmap.set(a.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){const t=this._map.get(e);return t&&typeof t=="object"&&"id"in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){const t=e._zod.parent;if(t){const a={...this.get(t)??{}};delete a.id;const i={...a,...this._map.get(e)};return Object.keys(i).length?i:void 0}return this._map.get(e)}has(e){return this._map.has(e)}}function yc(){return new fc}(Gi=globalThis).__zod_globalRegistry??(Gi.__zod_globalRegistry=yc());const nt=globalThis.__zod_globalRegistry;function vc(s,e){return new s({type:"string",...M(e)})}function bc(s,e){return new s({type:"string",format:"email",check:"string_format",abort:!1,...M(e)})}function ji(s,e){return new s({type:"string",format:"guid",check:"string_format",abort:!1,...M(e)})}function wc(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,...M(e)})}function Sc(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v4",...M(e)})}function _c(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v6",...M(e)})}function Ac(s,e){return new s({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v7",...M(e)})}function kc(s,e){return new s({type:"string",format:"url",check:"string_format",abort:!1,...M(e)})}function Tc(s,e){return new s({type:"string",format:"emoji",check:"string_format",abort:!1,...M(e)})}function Cc(s,e){return new s({type:"string",format:"nanoid",check:"string_format",abort:!1,...M(e)})}function Ic(s,e){return new s({type:"string",format:"cuid",check:"string_format",abort:!1,...M(e)})}function Ec(s,e){return new s({type:"string",format:"cuid2",check:"string_format",abort:!1,...M(e)})}function Mc(s,e){return new s({type:"string",format:"ulid",check:"string_format",abort:!1,...M(e)})}function Dc(s,e){return new s({type:"string",format:"xid",check:"string_format",abort:!1,...M(e)})}function Rc(s,e){return new s({type:"string",format:"ksuid",check:"string_format",abort:!1,...M(e)})}function Pc(s,e){return new s({type:"string",format:"ipv4",check:"string_format",abort:!1,...M(e)})}function xc(s,e){return new s({type:"string",format:"ipv6",check:"string_format",abort:!1,...M(e)})}function Nc(s,e){return new s({type:"string",format:"cidrv4",check:"string_format",abort:!1,...M(e)})}function $c(s,e){return new s({type:"string",format:"cidrv6",check:"string_format",abort:!1,...M(e)})}function Lc(s,e){return new s({type:"string",format:"base64",check:"string_format",abort:!1,...M(e)})}function Oc(s,e){return new s({type:"string",format:"base64url",check:"string_format",abort:!1,...M(e)})}function Bc(s,e){return new s({type:"string",format:"e164",check:"string_format",abort:!1,...M(e)})}function Fc(s,e){return new s({type:"string",format:"jwt",check:"string_format",abort:!1,...M(e)})}function Hc(s,e){return new s({type:"string",format:"datetime",check:"string_format",offset:!1,local:!1,precision:null,...M(e)})}function Wc(s,e){return new s({type:"string",format:"date",check:"string_format",...M(e)})}function zc(s,e){return new s({type:"string",format:"time",check:"string_format",precision:null,...M(e)})}function qc(s,e){return new s({type:"string",format:"duration",check:"string_format",...M(e)})}function Uc(s,e){return new s({type:"number",checks:[],...M(e)})}function Gc(s,e){return new s({type:"number",check:"number_format",abort:!1,format:"safeint",...M(e)})}function jc(s,e){return new s({type:"boolean",...M(e)})}function Vc(s){return new s({type:"unknown"})}function Yc(s,e){return new s({type:"never",...M(e)})}function Vi(s,e){return new en({check:"less_than",...M(e),value:s,inclusive:!1})}function Jt(s,e){return new en({check:"less_than",...M(e),value:s,inclusive:!0})}function Yi(s,e){return new tn({check:"greater_than",...M(e),value:s,inclusive:!1})}function Qt(s,e){return new tn({check:"greater_than",...M(e),value:s,inclusive:!0})}function Zi(s,e){return new ul({check:"multiple_of",...M(e),value:s})}function ln(s,e){return new hl({check:"max_length",...M(e),maximum:s})}function $t(s,e){return new pl({check:"min_length",...M(e),minimum:s})}function cn(s,e){return new ml({check:"length_equals",...M(e),length:s})}function Zc(s,e){return new gl({check:"string_format",format:"regex",...M(e),pattern:s})}function Xc(s){return new fl({check:"string_format",format:"lowercase",...M(s)})}function Kc(s){return new yl({check:"string_format",format:"uppercase",...M(s)})}function Jc(s,e){return new vl({check:"string_format",format:"includes",...M(e),includes:s})}function Qc(s,e){return new bl({check:"string_format",format:"starts_with",...M(e),prefix:s})}function eu(s,e){return new wl({check:"string_format",format:"ends_with",...M(e),suffix:s})}function et(s){return new Sl({check:"overwrite",tx:s})}function tu(s){return et(e=>e.normalize(s))}function au(){return et(s=>s.trim())}function iu(){return et(s=>s.toLowerCase())}function su(){return et(s=>s.toUpperCase())}function nu(){return et(s=>go(s))}function ru(s,e,t){return new s({type:"array",element:e,...M(t)})}function ou(s,e,t){return new s({type:"custom",check:"custom",fn:e,...M(t)})}function lu(s){const e=cu(t=>(t.addIssue=a=>{if(typeof a=="string")t.issues.push(ut(a,t.value,e._zod.def));else{const i=a;i.fatal&&(i.continue=!1),i.code??(i.code="custom"),i.input??(i.input=t.value),i.inst??(i.inst=e),i.continue??(i.continue=!e._zod.def.abort),t.issues.push(ut(i))}},s(t.value,t)));return e}function cu(s,e){const t=new me({check:"custom",...M(e)});return t._zod.check=s,t}function un(s){let e=s?.target??"draft-2020-12";return e==="draft-4"&&(e="draft-04"),e==="draft-7"&&(e="draft-07"),{processors:s.processors??{},metadataRegistry:s?.metadata??nt,target:e,unrepresentable:s?.unrepresentable??"throw",override:s?.override??(()=>{}),io:s?.io??"output",counter:0,seen:new Map,cycles:s?.cycles??"ref",reused:s?.reused??"inline",external:s?.external??void 0}}function K(s,e,t={path:[],schemaPath:[]}){var a;const i=s._zod.def,n=e.seen.get(s);if(n)return n.count++,t.schemaPath.includes(s)&&(n.cycle=t.path),n.schema;const r={schema:{},count:1,cycle:void 0,path:t.path};e.seen.set(s,r);const o=s._zod.toJSONSchema?.();if(o)r.schema=o;else{const u={...t,schemaPath:[...t.schemaPath,s],path:t.path};if(s._zod.processJSONSchema)s._zod.processJSONSchema(e,r.schema,u);else{const h=r.schema,p=e.processors[i.type];if(!p)throw new Error(`[toJSONSchema]: Non-representable type encountered: ${i.type}`);p(s,e,h,u)}const d=s._zod.parent;d&&(r.ref||(r.ref=d),K(d,e,u),e.seen.get(d).isParent=!0)}const l=e.metadataRegistry.get(s);return l&&Object.assign(r.schema,l),e.io==="input"&&ce(s)&&(delete r.schema.examples,delete r.schema.default),e.io==="input"&&r.schema._prefault&&((a=r.schema).default??(a.default=r.schema._prefault)),delete r.schema._prefault,e.seen.get(s).schema}function dn(s,e){const t=s.seen.get(e);if(!t)throw new Error("Unprocessed schema. This is a bug in Zod.");const a=new Map;for(const r of s.seen.entries()){const o=s.metadataRegistry.get(r[0])?.id;if(o){const l=a.get(o);if(l&&l!==r[0])throw new Error(`Duplicate schema id "${o}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);a.set(o,r[0])}}const i=r=>{const o=s.target==="draft-2020-12"?"$defs":"definitions";if(s.external){const d=s.external.registry.get(r[0])?.id,h=s.external.uri??(g=>g);if(d)return{ref:h(d)};const p=r[1].defId??r[1].schema.id??`schema${s.counter++}`;return r[1].defId=p,{defId:p,ref:`${h("__shared")}#/${o}/${p}`}}if(r[1]===t)return{ref:"#"};const c=`#/${o}/`,u=r[1].schema.id??`__schema${s.counter++}`;return{defId:u,ref:c+u}},n=r=>{if(r[1].schema.$ref)return;const o=r[1],{ref:l,defId:c}=i(r);o.def={...o.schema},c&&(o.defId=c);const u=o.schema;for(const d in u)delete u[d];u.$ref=l};if(s.cycles==="throw")for(const r of s.seen.entries()){const o=r[1];if(o.cycle)throw new Error(`Cycle detected: #/${o.cycle?.join("/")}/<root>
|
|
48
48
|
|
|
49
|
-
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(const r of s.seen.entries()){const o=r[1];if(e===r[0]){n(r);continue}if(s.external){const c=s.external.registry.get(r[0])?.id;if(e!==r[0]&&c){n(r);continue}}if(s.metadataRegistry.get(r[0])?.id){n(r);continue}if(o.cycle){n(r);continue}if(o.count>1&&s.reused==="ref"){n(r);continue}}}function hn(s,e){const t=s.seen.get(e);if(!t)throw new Error("Unprocessed schema. This is a bug in Zod.");const a=r=>{const o=s.seen.get(r);if(o.ref===null)return;const l=o.def??o.schema,c={...l},u=o.ref;if(o.ref=null,u){a(u);const h=s.seen.get(u),p=h.schema;if(p.$ref&&(s.target==="draft-07"||s.target==="draft-04"||s.target==="openapi-3.0")?(l.allOf=l.allOf??[],l.allOf.push(p)):Object.assign(l,p),Object.assign(l,c),r._zod.parent===u)for(const m in l)m==="$ref"||m==="allOf"||m in c||delete l[m];if(p.$ref&&h.def)for(const m in l)m==="$ref"||m==="allOf"||m in h.def&&JSON.stringify(l[m])===JSON.stringify(h.def[m])&&delete l[m]}const d=r._zod.parent;if(d&&d!==u){a(d);const h=s.seen.get(d);if(h?.schema.$ref&&(l.$ref=h.schema.$ref,h.def))for(const p in l)p==="$ref"||p==="allOf"||p in h.def&&JSON.stringify(l[p])===JSON.stringify(h.def[p])&&delete l[p]}s.override({zodSchema:r,jsonSchema:l,path:o.path??[]})};for(const r of[...s.seen.entries()].reverse())a(r[0]);const i={};if(s.target==="draft-2020-12"?i.$schema="https://json-schema.org/draft/2020-12/schema":s.target==="draft-07"?i.$schema="http://json-schema.org/draft-07/schema#":s.target==="draft-04"?i.$schema="http://json-schema.org/draft-04/schema#":s.target,s.external?.uri){const r=s.external.registry.get(e)?.id;if(!r)throw new Error("Schema is missing an `id` property");i.$id=s.external.uri(r)}Object.assign(i,t.def??t.schema);const n=s.external?.defs??{};for(const r of s.seen.entries()){const o=r[1];o.def&&o.defId&&(n[o.defId]=o.def)}s.external||Object.keys(n).length>0&&(s.target==="draft-2020-12"?i.$defs=n:i.definitions=n);try{const r=JSON.parse(JSON.stringify(i));return Object.defineProperty(r,"~standard",{value:{...e["~standard"],jsonSchema:{input:Lt(e,"input",s.processors),output:Lt(e,"output",s.processors)}},enumerable:!1,writable:!1}),r}catch{throw new Error("Error converting schema to JSON.")}}function ce(s,e){const t=e??{seen:new Set};if(t.seen.has(s))return!1;t.seen.add(s);const a=s._zod.def;if(a.type==="transform")return!0;if(a.type==="array")return ce(a.element,t);if(a.type==="set")return ce(a.valueType,t);if(a.type==="lazy")return ce(a.getter(),t);if(a.type==="promise"||a.type==="optional"||a.type==="nonoptional"||a.type==="nullable"||a.type==="readonly"||a.type==="default"||a.type==="prefault")return ce(a.innerType,t);if(a.type==="intersection")return ce(a.left,t)||ce(a.right,t);if(a.type==="record"||a.type==="map")return ce(a.keyType,t)||ce(a.valueType,t);if(a.type==="pipe")return ce(a.in,t)||ce(a.out,t);if(a.type==="object"){for(const i in a.shape)if(ce(a.shape[i],t))return!0;return!1}if(a.type==="union"){for(const i of a.options)if(ce(i,t))return!0;return!1}if(a.type==="tuple"){for(const i of a.items)if(ce(i,t))return!0;return!!(a.rest&&ce(a.rest,t))}return!1}const uu=(s,e={})=>t=>{const a=un({...t,processors:e});return K(s,a),dn(a,s),hn(a,s)},Lt=(s,e,t={})=>a=>{const{libraryOptions:i,target:n}=a??{},r=un({...i??{},target:n,io:e,processors:t});return K(s,r),dn(r,s),hn(r,s)},du={guid:"uuid",url:"uri",datetime:"date-time",json_string:"json-string",regex:""},hu=(s,e,t,a)=>{const i=t;i.type="string";const{minimum:n,maximum:r,format:o,patterns:l,contentEncoding:c}=s._zod.bag;if(typeof n=="number"&&(i.minLength=n),typeof r=="number"&&(i.maxLength=r),o&&(i.format=du[o]??o,i.format===""&&delete i.format,o==="time"&&delete i.format),c&&(i.contentEncoding=c),l&&l.size>0){const u=[...l];u.length===1?i.pattern=u[0].source:u.length>1&&(i.allOf=[...u.map(d=>({...e.target==="draft-07"||e.target==="draft-04"||e.target==="openapi-3.0"?{type:"string"}:{},pattern:d.source}))])}},pu=(s,e,t,a)=>{const i=t,{minimum:n,maximum:r,format:o,multipleOf:l,exclusiveMaximum:c,exclusiveMinimum:u}=s._zod.bag;typeof o=="string"&&o.includes("int")?i.type="integer":i.type="number",typeof u=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(i.minimum=u,i.exclusiveMinimum=!0):i.exclusiveMinimum=u),typeof n=="number"&&(i.minimum=n,typeof u=="number"&&e.target!=="draft-04"&&(u>=n?delete i.minimum:delete i.exclusiveMinimum)),typeof c=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(i.maximum=c,i.exclusiveMaximum=!0):i.exclusiveMaximum=c),typeof r=="number"&&(i.maximum=r,typeof c=="number"&&e.target!=="draft-04"&&(c<=r?delete i.maximum:delete i.exclusiveMaximum)),typeof l=="number"&&(i.multipleOf=l)},mu=(s,e,t,a)=>{t.type="boolean"},gu=(s,e,t,a)=>{t.not={}},fu=(s,e,t,a)=>{},yu=(s,e,t,a)=>{const i=s._zod.def,n=qs(i.entries);n.every(r=>typeof r=="number")&&(t.type="number"),n.every(r=>typeof r=="string")&&(t.type="string"),t.enum=n},vu=(s,e,t,a)=>{if(e.unrepresentable==="throw")throw new Error("Custom types cannot be represented in JSON Schema")},bu=(s,e,t,a)=>{if(e.unrepresentable==="throw")throw new Error("Transforms cannot be represented in JSON Schema")},wu=(s,e,t,a)=>{const i=t,n=s._zod.def,{minimum:r,maximum:o}=s._zod.bag;typeof r=="number"&&(i.minItems=r),typeof o=="number"&&(i.maxItems=o),i.type="array",i.items=K(n.element,e,{...a,path:[...a.path,"items"]})},Su=(s,e,t,a)=>{const i=t,n=s._zod.def;i.type="object",i.properties={};const r=n.shape;for(const c in r)i.properties[c]=K(r[c],e,{...a,path:[...a.path,"properties",c]});const o=new Set(Object.keys(r)),l=new Set([...o].filter(c=>{const u=n.shape[c]._zod;return e.io==="input"?u.optin===void 0:u.optout===void 0}));l.size>0&&(i.required=Array.from(l)),n.catchall?._zod.def.type==="never"?i.additionalProperties=!1:n.catchall?n.catchall&&(i.additionalProperties=K(n.catchall,e,{...a,path:[...a.path,"additionalProperties"]})):e.io==="output"&&(i.additionalProperties=!1)},_u=(s,e,t,a)=>{const i=s._zod.def,n=i.inclusive===!1,r=i.options.map((o,l)=>K(o,e,{...a,path:[...a.path,n?"oneOf":"anyOf",l]}));n?t.oneOf=r:t.anyOf=r},Au=(s,e,t,a)=>{const i=s._zod.def,n=K(i.left,e,{...a,path:[...a.path,"allOf",0]}),r=K(i.right,e,{...a,path:[...a.path,"allOf",1]}),o=c=>"allOf"in c&&Object.keys(c).length===1,l=[...o(n)?n.allOf:[n],...o(r)?r.allOf:[r]];t.allOf=l},ku=(s,e,t,a)=>{const i=t,n=s._zod.def;i.type="object";const r=n.keyType,l=r._zod.bag?.patterns;if(n.mode==="loose"&&l&&l.size>0){const u=K(n.valueType,e,{...a,path:[...a.path,"patternProperties","*"]});i.patternProperties={};for(const d of l)i.patternProperties[d.source]=u}else(e.target==="draft-07"||e.target==="draft-2020-12")&&(i.propertyNames=K(n.keyType,e,{...a,path:[...a.path,"propertyNames"]})),i.additionalProperties=K(n.valueType,e,{...a,path:[...a.path,"additionalProperties"]});const c=r._zod.values;if(c){const u=[...c].filter(d=>typeof d=="string"||typeof d=="number");u.length>0&&(i.required=u)}},Tu=(s,e,t,a)=>{const i=s._zod.def,n=K(i.innerType,e,a),r=e.seen.get(s);e.target==="openapi-3.0"?(r.ref=i.innerType,t.nullable=!0):t.anyOf=[n,{type:"null"}]},Cu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType},Iu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,t.default=JSON.parse(JSON.stringify(i.defaultValue))},Eu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,e.io==="input"&&(t._prefault=JSON.parse(JSON.stringify(i.defaultValue)))},Mu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType;let r;try{r=i.catchValue(void 0)}catch{throw new Error("Dynamic catch values are not supported in JSON Schema")}t.default=r},Du=(s,e,t,a)=>{const i=s._zod.def,n=e.io==="input"?i.in._zod.def.type==="transform"?i.out:i.in:i.out;K(n,e,a);const r=e.seen.get(s);r.ref=n},Ru=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,t.readOnly=!0},pn=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType},Pu=b("ZodISODateTime",(s,e)=>{$l.init(s,e),j.init(s,e)});function xu(s){return Hc(Pu,s)}const Nu=b("ZodISODate",(s,e)=>{Ll.init(s,e),j.init(s,e)});function $u(s){return Wc(Nu,s)}const Lu=b("ZodISOTime",(s,e)=>{Ol.init(s,e),j.init(s,e)});function Ou(s){return zc(Lu,s)}const Bu=b("ZodISODuration",(s,e)=>{Bl.init(s,e),j.init(s,e)});function Fu(s){return qc(Bu,s)}const Hu=(s,e)=>{Vs.init(s,e),s.name="ZodError",Object.defineProperties(s,{format:{value:t=>Eo(s,t)},flatten:{value:t=>Io(s,t)},addIssue:{value:t=>{s.issues.push(t),s.message=JSON.stringify(s.issues,ua,2)}},addIssues:{value:t=>{s.issues.push(...t),s.message=JSON.stringify(s.issues,ua,2)}},isEmpty:{get(){return s.issues.length===0}}})},ve=b("ZodError",Hu,{Parent:Error}),Wu=Oa(ve),zu=Ba(ve),qu=Ut(ve),Uu=Gt(ve),Gu=Ro(ve),ju=Po(ve),Vu=xo(ve),Yu=No(ve),Zu=$o(ve),Xu=Lo(ve),Ku=Oo(ve),Ju=Bo(ve),Z=b("ZodType",(s,e)=>(Y.init(s,e),Object.assign(s["~standard"],{jsonSchema:{input:Lt(s,"input"),output:Lt(s,"output")}}),s.toJSONSchema=uu(s,{}),s.def=e,s.type=e.type,Object.defineProperty(s,"_def",{value:e}),s.check=(...t)=>s.clone($e(e,{checks:[...e.checks??[],...t.map(a=>typeof a=="function"?{_zod:{check:a,def:{check:"custom"},onattach:[]}}:a)]}),{parent:!0}),s.with=s.check,s.clone=(t,a)=>Le(s,t,a),s.brand=()=>s,s.register=((t,a)=>(t.add(s,a),s)),s.parse=(t,a)=>Wu(s,t,a,{callee:s.parse}),s.safeParse=(t,a)=>qu(s,t,a),s.parseAsync=async(t,a)=>zu(s,t,a,{callee:s.parseAsync}),s.safeParseAsync=async(t,a)=>Uu(s,t,a),s.spa=s.safeParseAsync,s.encode=(t,a)=>Gu(s,t,a),s.decode=(t,a)=>ju(s,t,a),s.encodeAsync=async(t,a)=>Vu(s,t,a),s.decodeAsync=async(t,a)=>Yu(s,t,a),s.safeEncode=(t,a)=>Zu(s,t,a),s.safeDecode=(t,a)=>Xu(s,t,a),s.safeEncodeAsync=async(t,a)=>Ku(s,t,a),s.safeDecodeAsync=async(t,a)=>Ju(s,t,a),s.refine=(t,a)=>s.check(Ud(t,a)),s.superRefine=t=>s.check(Gd(t)),s.overwrite=t=>s.check(et(t)),s.optional=()=>Qi(s),s.exactOptional=()=>Rd(s),s.nullable=()=>es(s),s.nullish=()=>Qi(es(s)),s.nonoptional=t=>Od(s,t),s.array=()=>ne(s),s.or=t=>Ha([s,t]),s.and=t=>Cd(s,t),s.transform=t=>ts(s,Md(t)),s.default=t=>Nd(s,t),s.prefault=t=>Ld(s,t),s.catch=t=>Fd(s,t),s.pipe=t=>ts(s,t),s.readonly=()=>zd(s),s.describe=t=>{const a=s.clone();return nt.add(a,{description:t}),a},Object.defineProperty(s,"description",{get(){return nt.get(s)?.description},configurable:!0}),s.meta=(...t)=>{if(t.length===0)return nt.get(s);const a=s.clone();return nt.add(a,t[0]),a},s.isOptional=()=>s.safeParse(void 0).success,s.isNullable=()=>s.safeParse(null).success,s.apply=t=>t(s),s)),mn=b("_ZodString",(s,e)=>{Fa.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>hu(s,a,i);const t=s._zod.bag;s.format=t.format??null,s.minLength=t.minimum??null,s.maxLength=t.maximum??null,s.regex=(...a)=>s.check(Zc(...a)),s.includes=(...a)=>s.check(Jc(...a)),s.startsWith=(...a)=>s.check(Qc(...a)),s.endsWith=(...a)=>s.check(eu(...a)),s.min=(...a)=>s.check($t(...a)),s.max=(...a)=>s.check(ln(...a)),s.length=(...a)=>s.check(cn(...a)),s.nonempty=(...a)=>s.check($t(1,...a)),s.lowercase=a=>s.check(Xc(a)),s.uppercase=a=>s.check(Kc(a)),s.trim=()=>s.check(au()),s.normalize=(...a)=>s.check(tu(...a)),s.toLowerCase=()=>s.check(iu()),s.toUpperCase=()=>s.check(su()),s.slugify=()=>s.check(nu())}),Qu=b("ZodString",(s,e)=>{Fa.init(s,e),mn.init(s,e),s.email=t=>s.check(bc(ed,t)),s.url=t=>s.check(kc(td,t)),s.jwt=t=>s.check(Fc(fd,t)),s.emoji=t=>s.check(Tc(ad,t)),s.guid=t=>s.check(ji(Xi,t)),s.uuid=t=>s.check(wc(Tt,t)),s.uuidv4=t=>s.check(Sc(Tt,t)),s.uuidv6=t=>s.check(_c(Tt,t)),s.uuidv7=t=>s.check(Ac(Tt,t)),s.nanoid=t=>s.check(Cc(id,t)),s.guid=t=>s.check(ji(Xi,t)),s.cuid=t=>s.check(Ic(sd,t)),s.cuid2=t=>s.check(Ec(nd,t)),s.ulid=t=>s.check(Mc(rd,t)),s.base64=t=>s.check(Lc(pd,t)),s.base64url=t=>s.check(Oc(md,t)),s.xid=t=>s.check(Dc(od,t)),s.ksuid=t=>s.check(Rc(ld,t)),s.ipv4=t=>s.check(Pc(cd,t)),s.ipv6=t=>s.check(xc(ud,t)),s.cidrv4=t=>s.check(Nc(dd,t)),s.cidrv6=t=>s.check($c(hd,t)),s.e164=t=>s.check(Bc(gd,t)),s.datetime=t=>s.check(xu(t)),s.date=t=>s.check($u(t)),s.time=t=>s.check(Ou(t)),s.duration=t=>s.check(Fu(t))});function T(s){return vc(Qu,s)}const j=b("ZodStringFormat",(s,e)=>{G.init(s,e),mn.init(s,e)}),ed=b("ZodEmail",(s,e)=>{Cl.init(s,e),j.init(s,e)}),Xi=b("ZodGUID",(s,e)=>{kl.init(s,e),j.init(s,e)}),Tt=b("ZodUUID",(s,e)=>{Tl.init(s,e),j.init(s,e)}),td=b("ZodURL",(s,e)=>{Il.init(s,e),j.init(s,e)}),ad=b("ZodEmoji",(s,e)=>{El.init(s,e),j.init(s,e)}),id=b("ZodNanoID",(s,e)=>{Ml.init(s,e),j.init(s,e)}),sd=b("ZodCUID",(s,e)=>{Dl.init(s,e),j.init(s,e)}),nd=b("ZodCUID2",(s,e)=>{Rl.init(s,e),j.init(s,e)}),rd=b("ZodULID",(s,e)=>{Pl.init(s,e),j.init(s,e)}),od=b("ZodXID",(s,e)=>{xl.init(s,e),j.init(s,e)}),ld=b("ZodKSUID",(s,e)=>{Nl.init(s,e),j.init(s,e)}),cd=b("ZodIPv4",(s,e)=>{Fl.init(s,e),j.init(s,e)}),ud=b("ZodIPv6",(s,e)=>{Hl.init(s,e),j.init(s,e)}),dd=b("ZodCIDRv4",(s,e)=>{Wl.init(s,e),j.init(s,e)}),hd=b("ZodCIDRv6",(s,e)=>{zl.init(s,e),j.init(s,e)}),pd=b("ZodBase64",(s,e)=>{ql.init(s,e),j.init(s,e)}),md=b("ZodBase64URL",(s,e)=>{Gl.init(s,e),j.init(s,e)}),gd=b("ZodE164",(s,e)=>{jl.init(s,e),j.init(s,e)}),fd=b("ZodJWT",(s,e)=>{Yl.init(s,e),j.init(s,e)}),gn=b("ZodNumber",(s,e)=>{sn.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>pu(s,a,i),s.gt=(a,i)=>s.check(Yi(a,i)),s.gte=(a,i)=>s.check(Qt(a,i)),s.min=(a,i)=>s.check(Qt(a,i)),s.lt=(a,i)=>s.check(Vi(a,i)),s.lte=(a,i)=>s.check(Jt(a,i)),s.max=(a,i)=>s.check(Jt(a,i)),s.int=a=>s.check(Ki(a)),s.safe=a=>s.check(Ki(a)),s.positive=a=>s.check(Yi(0,a)),s.nonnegative=a=>s.check(Qt(0,a)),s.negative=a=>s.check(Vi(0,a)),s.nonpositive=a=>s.check(Jt(0,a)),s.multipleOf=(a,i)=>s.check(Zi(a,i)),s.step=(a,i)=>s.check(Zi(a,i)),s.finite=()=>s;const t=s._zod.bag;s.minValue=Math.max(t.minimum??Number.NEGATIVE_INFINITY,t.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,s.maxValue=Math.min(t.maximum??Number.POSITIVE_INFINITY,t.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,s.isInt=(t.format??"").includes("int")||Number.isSafeInteger(t.multipleOf??.5),s.isFinite=!0,s.format=t.format??null});function A(s){return Uc(gn,s)}const yd=b("ZodNumberFormat",(s,e)=>{Zl.init(s,e),gn.init(s,e)});function Ki(s){return Gc(yd,s)}const vd=b("ZodBoolean",(s,e)=>{Xl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>mu(s,t,a)});function ha(s){return jc(vd,s)}const bd=b("ZodUnknown",(s,e)=>{Kl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>fu()});function Ji(){return Vc(bd)}const wd=b("ZodNever",(s,e)=>{Jl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>gu(s,t,a)});function Sd(s){return Yc(wd,s)}const _d=b("ZodArray",(s,e)=>{Ql.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>wu(s,t,a,i),s.element=e.element,s.min=(t,a)=>s.check($t(t,a)),s.nonempty=t=>s.check($t(1,t)),s.max=(t,a)=>s.check(ln(t,a)),s.length=(t,a)=>s.check(cn(t,a)),s.unwrap=()=>s.element});function ne(s,e){return ru(_d,s,e)}const Ad=b("ZodObject",(s,e)=>{tc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Su(s,t,a,i),B(s,"shape",()=>e.shape),s.keyof=()=>It(Object.keys(s._zod.def.shape)),s.catchall=t=>s.clone({...s._zod.def,catchall:t}),s.passthrough=()=>s.clone({...s._zod.def,catchall:Ji()}),s.loose=()=>s.clone({...s._zod.def,catchall:Ji()}),s.strict=()=>s.clone({...s._zod.def,catchall:Sd()}),s.strip=()=>s.clone({...s._zod.def,catchall:void 0}),s.extend=t=>_o(s,t),s.safeExtend=t=>Ao(s,t),s.merge=t=>ko(s,t),s.pick=t=>wo(s,t),s.omit=t=>So(s,t),s.partial=(...t)=>To(fn,s,t[0]),s.required=(...t)=>Co(yn,s,t[0])});function W(s,e){const t={type:"object",shape:s??{},...M(e)};return new Ad(t)}const kd=b("ZodUnion",(s,e)=>{ac.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>_u(s,t,a,i),s.options=e.options});function Ha(s,e){return new kd({type:"union",options:s,...M(e)})}const Td=b("ZodIntersection",(s,e)=>{ic.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Au(s,t,a,i)});function Cd(s,e){return new Td({type:"intersection",left:s,right:e})}const Id=b("ZodRecord",(s,e)=>{sc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>ku(s,t,a,i),s.keyType=e.keyType,s.valueType=e.valueType});function Ct(s,e,t){return new Id({type:"record",keyType:s,valueType:e,...M(t)})}const pa=b("ZodEnum",(s,e)=>{nc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>yu(s,a,i),s.enum=e.entries,s.options=Object.values(e.entries);const t=new Set(Object.keys(e.entries));s.extract=(a,i)=>{const n={};for(const r of a)if(t.has(r))n[r]=e.entries[r];else throw new Error(`Key ${r} not found in enum`);return new pa({...e,checks:[],...M(i),entries:n})},s.exclude=(a,i)=>{const n={...e.entries};for(const r of a)if(t.has(r))delete n[r];else throw new Error(`Key ${r} not found in enum`);return new pa({...e,checks:[],...M(i),entries:n})}});function It(s,e){const t=Array.isArray(s)?Object.fromEntries(s.map(a=>[a,a])):s;return new pa({type:"enum",entries:t,...M(e)})}const Ed=b("ZodTransform",(s,e)=>{rc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>bu(s,t),s._zod.parse=(t,a)=>{if(a.direction==="backward")throw new Ws(s.constructor.name);t.addIssue=n=>{if(typeof n=="string")t.issues.push(ut(n,t.value,e));else{const r=n;r.fatal&&(r.continue=!1),r.code??(r.code="custom"),r.input??(r.input=t.value),r.inst??(r.inst=s),t.issues.push(ut(r))}};const i=e.transform(t.value,t);return i instanceof Promise?i.then(n=>(t.value=n,t)):(t.value=i,t)}});function Md(s){return new Ed({type:"transform",transform:s})}const fn=b("ZodOptional",(s,e)=>{on.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>pn(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Qi(s){return new fn({type:"optional",innerType:s})}const Dd=b("ZodExactOptional",(s,e)=>{oc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>pn(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Rd(s){return new Dd({type:"optional",innerType:s})}const Pd=b("ZodNullable",(s,e)=>{lc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Tu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function es(s){return new Pd({type:"nullable",innerType:s})}const xd=b("ZodDefault",(s,e)=>{cc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Iu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType,s.removeDefault=s.unwrap});function Nd(s,e){return new xd({type:"default",innerType:s,get defaultValue(){return typeof e=="function"?e():Gs(e)}})}const $d=b("ZodPrefault",(s,e)=>{uc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Eu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Ld(s,e){return new $d({type:"prefault",innerType:s,get defaultValue(){return typeof e=="function"?e():Gs(e)}})}const yn=b("ZodNonOptional",(s,e)=>{dc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Cu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Od(s,e){return new yn({type:"nonoptional",innerType:s,...M(e)})}const Bd=b("ZodCatch",(s,e)=>{hc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Mu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType,s.removeCatch=s.unwrap});function Fd(s,e){return new Bd({type:"catch",innerType:s,catchValue:typeof e=="function"?e:()=>e})}const Hd=b("ZodPipe",(s,e)=>{pc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Du(s,t,a,i),s.in=e.in,s.out=e.out});function ts(s,e){return new Hd({type:"pipe",in:s,out:e})}const Wd=b("ZodReadonly",(s,e)=>{mc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Ru(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function zd(s){return new Wd({type:"readonly",innerType:s})}const qd=b("ZodCustom",(s,e)=>{gc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>vu(s,t)});function Ud(s,e={}){return ou(qd,s,e)}function Gd(s){return lu(s)}const jd=Re.map(s=>s),vn=W({id:T(),uuid:T(),playlist_index:A().nonnegative(),chain_name:T(),token_address:T().optional(),token_id:T().optional(),tx_id:T().optional(),platform:T(),title:T(),artist:T(),description:T().optional(),album:T().optional(),image_url:T().url(),image_thumb_url:T().url().optional(),audio_url:T().url(),audio_url_lossless:T().url().optional(),duration:A().nonnegative(),genre:T(),tags:ne(T()),bpm:A().positive().optional(),key:T().optional(),attributes:Ct(T(),Ha([T(),A()])).optional()}).refine(s=>s.chain_name==="AR"?!!s.tx_id:!!s.token_address&&!!s.token_id,{message:"AR chain tracks must have tx_id; other chains must have token_address and token_id",path:["chain_name"]}),Vd=W({name:T(),description:T().optional(),image:T().url(),creator:T(),genre:T().optional(),tags:ne(T()).optional(),tracks:ne(vn)}),Yd=W({bass_dominance:A().min(0).max(1),mid_dominance:A().min(0).max(1),treble_dominance:A().min(0).max(1),average_amplitude:A().min(0).max(1),spectral_centroid:A().optional(),spectral_rolloff:A().optional(),zero_crossing_rate:A().optional(),color_palette:W({colors:ne(T()),primary_color:T(),secondary_color:T().optional(),accent_color:T().optional(),brightness:A().min(0).max(1),saturation:A().min(0).max(1),is_monochrome:ha()}).optional(),analysis_metadata:W({duration_analyzed:A().positive(),full_buffer_analyzed:ha(),sample_positions:ne(A()),analyzed_at:T()})}),ma=W({STR:A().min(1).max(20),DEX:A().min(1).max(20),CON:A().min(1).max(20),INT:A().min(1).max(20),WIS:A().min(1).max(20),CHA:A().min(1).max(20)}),Zd=W({name:T(),race:It(["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"]),class:It(jd),level:A().min(1).max(20),ability_scores:ma,ability_modifiers:ma,proficiency_bonus:A().min(2).max(6),hp:W({current:A().nonnegative(),max:A().positive(),temp:A().nonnegative()}),armor_class:A().positive(),initiative:A(),speed:A().positive(),skills:Ct(T(),It(["none","proficient","expertise"])),saving_throws:Ct(T(),ha()),racial_traits:ne(T()),class_features:ne(T()),spells:W({spell_slots:Ct(T(),W({total:A().nonnegative(),used:A().nonnegative()})),known_spells:ne(T()),cantrips:ne(T())}).optional(),equipment:W({weapons:ne(T()),armor:ne(T()),items:ne(T())}).optional(),appearance:W({body_type:T(),skin_tone:T(),hair_style:T(),hair_color:T(),eye_color:T(),facial_features:ne(T()),primary_color:T().optional(),secondary_color:T().optional(),aura_color:T().optional()}).optional(),xp:W({current:A().nonnegative(),next_level:A().positive()}),seed:T(),generated_at:T()});function Xd(s){if(!s||!s.metadata)return null;if(typeof s.metadata=="string")try{const e=JSON.parse(s.metadata);return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:null}catch{return null}return typeof s.metadata=="object"&&!Array.isArray(s.metadata)?s.metadata:null}function bn(s){const e={stems:[],mixes:[],hasExtras:!1};if(!s)return e;const t=th(s),a=ah(s),i=typeof s.vrm=="string"?s.vrm:void 0,n=Jd(s.lyrics),r=as(s.visualizer),o=as(s.video),l=Qd(s.merch),c=eh(s.credits),u=typeof s.midi=="string"?s.midi:void 0,d=typeof s.step_mania=="string"?s.step_mania:void 0,h=typeof s.clone_hero=="string"?s.clone_hero:void 0,p=typeof s.external_url=="string"?s.external_url:void 0;return{hasExtras:t.length>0||a.length>0||!!i||!!n||!!r||!!o||!!l||!!c||!!u||!!d||!!h||!!p,...t.length>0?{stems:t}:{},...a.length>0?{mixes:a}:{},...i?{vrm:i}:{},...n?{lyrics:n}:{},...r?{visualizer:r}:{},...o?{video:o}:{},...l?{merch:l}:{},...c?{credits:c}:{},...u?{midi:u}:{},...d?{step_mania:d}:{},...h?{clone_hero:h}:{},...p?{external_url:p}:{}}}const Kd=["audio/wav","audio/x-wav","audio/flac"];function wn(s,e,t){const a=e?.["Selected Mix"],i=typeof s=="string"&&s||typeof a=="string"&&a||null;if(!i||i==="default")return null;const n=(t??[]).filter(c=>c.name===i);if(n.length===0)return null;const r=c=>Kd.includes((c.mime_type??"").toLowerCase()),o=n.find(r),l=n.find(c=>!r(c))??n[0];return{name:i,...l?.uri?{audio_url:l.uri}:{},...o?.uri&&o.uri!==l?.uri?{audio_url_lossless:o.uri}:{}}}function Jd(s){if(s){if(typeof s=="string")return{text:s};if(typeof s=="object"&&!Array.isArray(s)){const e=s;return{text:typeof e.text=="string"?e.text:void 0}}}}function as(s){if(!s||typeof s!="object"||Array.isArray(s))return;const e=s;if(typeof e.uri=="string")return{uri:e.uri,mime_type:typeof e.mime_type=="string"?e.mime_type:void 0}}function Qd(s){if(!s||typeof s!="object"||Array.isArray(s))return;const e=s;if(typeof e.uri=="string")return{uri:e.uri,mime_type:typeof e.mime_type=="string"?e.mime_type:void 0,type:typeof e.type=="string"?e.type:void 0}}function eh(s){if(!Array.isArray(s))return;const e=[];for(const t of s)if(t&&typeof t=="object"){const a=t;typeof a.name=="string"&&typeof a.credit=="string"&&e.push({name:a.name,credit:a.credit})}return e.length>0?e:void 0}function th(s){const e=s.stems;if(!Array.isArray(e))return[];const t=[];for(const a of e)if(a&&typeof a=="object"){const i=a;t.push({name:typeof i.name=="string"?i.name:"",uri:typeof i.uri=="string"?i.uri:void 0,mime_type:typeof i.mime_type=="string"?i.mime_type:void 0})}return t}function ah(s){const e=s.mixes;if(!Array.isArray(e))return[];const t=[];for(const a of e)if(a&&typeof a=="object"){const i=a,n=ih(i.name),r=sh(i.conditions);t.push({name:n||"",uri:typeof i.uri=="string"?i.uri:void 0,mime_type:typeof i.mime_type=="string"?i.mime_type:void 0,conditions:r})}return t}function ih(s){if(typeof s=="string")return s;if(s&&typeof s=="object"&&!Array.isArray(s)){const e=s;if(typeof e.value=="string")return e.value}return""}function sh(s){if(!Array.isArray(s))return[];const e=[];for(const t of s)if(t&&typeof t=="object"){const a=t;typeof a.type=="string"&&typeof a.value=="string"&&e.push({type:a.type,value:a.value})}return e}function nh(s,e){const t=[],a=new Date;for(const i of s.mixes||[]){if(i.conditions.length===0){t.push({mix:i,conditions:[],allMet:!0,unmetConditions:[]});continue}const n=[],r=[];for(const o of i.conditions){const l=rh(o,e,a);n.push(l),l.met||r.push(l)}t.push({mix:i,conditions:n,allMet:r.length===0,unmetConditions:r})}return t}function rh(s,e,t=new Date){const{type:a,value:i}=s,n=e?.environment,r=e?.appState;switch(a){case"weather":{const o=n?.weather?.weatherType||"",l=o.toLowerCase()===i.toLowerCase();return{type:a,value:i,met:l,reason:l?`Weather is ${o}`:`Weather is ${o} (need ${i})`}}case"day":{const l=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][t.getDay()],c=l.toLowerCase()===i.toLowerCase();return{type:a,value:i,met:c,reason:c?`Today is ${l}`:`Today is ${l} (need ${i})`}}case"start_time":{const o=t.getHours()*60+t.getMinutes(),l=is(i),c=o>=l;return{type:a,value:i,met:c,reason:c?`Time is after ${i}`:`Time is before ${i}`}}case"end_time":{const o=t.getHours()*60+t.getMinutes(),l=is(i),c=o<l;return{type:a,value:i,met:c,reason:c?`Time is before ${i}`:`Time is after ${i}`}}case"min_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=l>=o;return{type:a,value:i,met:c,reason:c?`Played ${l} times (need ${o})`:`Played ${l} times (need ${o})`}}case"max_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=l<=o;return{type:a,value:i,met:c,reason:c?`Played ${l} times (max ${o})`:`Played ${l} times (exceeds max ${o})`}}case"every_x_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=o>0&&l>0&&l%o===0;return{type:a,value:i,met:c,reason:c?`Play #${l} (every ${o} plays)`:`Play #${l} (not a multiple of ${o})`}}case"altitude":{const o=n?.geolocation?.altitude;if(o==null)return{type:a,value:i,met:!1,reason:"Altitude unavailable"};const l=oh(i),c=lh(o,l.operator,l.threshold);return{type:a,value:i,met:c,reason:c?`Altitude ${o}m ${l.operator} ${l.threshold}m`:`Altitude ${o}m (need ${l.operator} ${l.threshold}m)`}}case"favorite":{const o=r?.isFavorite??!1,l=i.toLowerCase()==="true",c=o===l;return{type:a,value:i,met:c,reason:c?o?"Track is favorited":"Track is not favorited":o?"Track is favorited (need unfavorited)":"Track is not favorited (need favorited)"}}case"birthday":{if(!r?.userBirthday)return{type:a,value:i,met:!1,reason:"Birthday not set"};const l=`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`===r.userBirthday;return{type:a,value:i,met:l,reason:l?"It's your birthday!":`Not your birthday (need ${r.userBirthday})`}}case"weight":return{type:a,value:i,met:!0,reason:`Weight ${i} (used for random selection, always available)`};default:return{type:a,value:i,met:!0,reason:`Custom condition: ${a} = ${i}`}}}function is(s){const e=s.split(":");if(e.length!==2)return 0;const t=parseInt(e[0],10),a=parseInt(e[1],10);return isNaN(t)||isNaN(a)?0:t*60+a}function oh(s){const e=s.match(/^(>=|<=|>|<|=)?\s*(\d+(?:\.\d+)?)\s*$/);return e?{operator:e[1]||">=",threshold:parseFloat(e[2])}:{operator:">=",threshold:parseFloat(s)||0}}function lh(s,e,t){switch(e){case">":return s>t;case">=":return s>=t;case"<":return s<t;case"<=":return s<=t;case"=":case"==":return s===t;default:return s>=t}}class Sn{generateName(e,t,a,i,n=!1){const r=this.cleanTitle(t.title),o=n?e:`${e}-${Date.now()}-${Math.random()}`,l=new Se(o);switch(this.selectFormat(l)){case"class_title":return this.formatClassTitle(r,i,l);case"adjective_construct":return this.formatAdjectiveConstruct(r,t.genre,a,l);case"clan_construct":return this.formatClanConstruct(r,t.artist,l);case"descriptive_epithet":return this.formatDescriptiveEpithet(r,i,a,l);case"compound_adjective":return this.formatCompoundAdjective(r,t.genre,a,l);case"artist_inspired":return this.formatArtistInspired(r,t.artist,i,l);case"mononym_subtitle":return this.formatMononymSubtitle(r,a,t.genre,l);default:return r}}cleanTitle(e){let t=e;return t=t.replace(/\s*[([].*?(video|official|remix|mix|edit|feat|ft\.|version|remaster).*?[)\]]/gi,""),t=t.replace(/^\s*\d+[\s.-]+/,""),t=t.replace(/\.(mp3|wav|flac|m4a)$/i,""),t.trim()}selectFormat(e){const t=e.random();return t<.2?"class_title":t<.4?"adjective_construct":t<.5?"clan_construct":t<.7?"descriptive_epithet":t<.85?"compound_adjective":t<.95?"artist_inspired":"mononym_subtitle"}formatClassTitle(e,t,a){return`${e} the ${t}`}formatAdjectiveConstruct(e,t,a,i){const n=this.findGenreKey(t),r=Ae.adjectives[n]||Ae.adjectives.default,o=this.calculateBalancedAdjectiveWeights(a),l=i.weightedChoice(o),c=r[l];return`${i.randomChoice(c)} ${e}`}formatClanConstruct(e,t,a){return`${e} of ${t}`}formatDescriptiveEpithet(e,t,a,i){const n=i.randomChoice(Ae.descriptors),r=Ae.classAspects[t]||["Wanderer","Seeker","Sage","Hero"],o=i.randomChoice(r);return`${e}, the ${n} ${o}`}formatCompoundAdjective(e,t,a,i){const n=i.randomChoice(Ae.prefixes),r=i.randomChoice(Ae.suffixes);return`${n}-${r} ${e}`}formatArtistInspired(e,t,a,i){const n=t.split(" ")[0],r=i.randomChoice(Ae.occupations),o=`${n}${r}`,l=i.randomChoice(Ae.realms);return`${o} of the ${l}`}formatMononymSubtitle(e,t,a,i){const n=e.split(" "),r=n[0];let o;return n.length>1?o=n.slice(1).join(" "):o=i.randomChoice(Ae.subtitlePrefixes),`${r} [${o}]`}calculateBalancedAdjectiveWeights(e){const a={bass:e.bass_dominance,treble:e.treble_dominance,mid:e.mid_dominance,quiet:e.average_amplitude<.4?.5:0,loud:e.average_amplitude>.6?.5:0};return[["bass",1+a.bass],["treble",1+a.treble],["mid",1+a.mid],["quiet",1+a.quiet],["loud",1+a.loud]]}findGenreKey(e){const t=e.toLowerCase(),a=Object.keys(Ae.adjectives).filter(i=>i!=="default"&&t.includes(i));return a.length>0?a[0]:"default"}}class ye{static select(e,t,a,i="relative"){if(e.length===0)throw new Error("Cannot select from empty items array");const n=this.getFinalWeights(e,t,i),r=e.map(o=>{const l=this.getItemKey(o),c=l in n?n[l]:1;return[o,c]});return a.weightedChoice(r)}static selectMultiple(e,t,a,i,n="relative"){if(e.length===0)return[];if(i<=0)return[];if(i>=e.length)return a.shuffle(e);const r=[],o=[...e],l={...t};for(let c=0;c<i&&o.length>0;c++){const u=this.select(o,l,a,n);r.push(u);const d=o.indexOf(u);if(d!==-1){const h=this.getItemKey(u);o.splice(d,1),delete l[h]}}return r}static getProbabilities(e,t,a="relative"){if(e.length===0)return{};const i=this.getFinalWeights(e,t,a),n=Object.values(i).reduce((o,l)=>o+l,0);if(n===0){const o={};for(const l of e){const c=this.getItemKey(l);o[c]=1/e.length}return o}const r={};for(const o of e){const l=this.getItemKey(o),c=i[l]||0;r[l]=n>0?c/n:0}return r}static normalizeWeights(e,t,a="relative"){if(e.length===0)return{};const i=ye.getFinalWeights(e,t,a),n=Object.values(i).reduce((o,l)=>o+l,0);if(n===0){const o={};for(const l of e){const c=ye.getItemKey(l);o[c]=1/e.length}return o}const r={};for(const o of e){const l=ye.getItemKey(o);r[l]=i[l]/n}return r}static getItemKey(e){return typeof e=="string"?e:e&&typeof e=="object"&&"name"in e?String(e.name):String(e)}static getFinalWeights(e,t,a){const i={};if(a==="default")for(const n of e){const r=this.getItemKey(n);i[r]=1}else if(a==="absolute")for(const n of e){const r=this.getItemKey(n);i[r]=t[r]!==void 0?t[r]:1}else for(const n of e){const r=this.getItemKey(n);i[r]=t[r]!==void 0?t[r]:1}return i}}const ga=[{id:"barbarian_rage",name:"Rage",description:"In battle, you fight with primal fury. On your turn, you can enter a rage as a bonus action. While raging, you gain the following benefits if you aren't wearing heavy armor: Advantage on Strength checks and Strength saving throws, +2 damage bonus with melee weapons using Strength, resistance to bludgeoning, piercing, and slashing damage.",type:"active",level:1,class:"Barbarian",effects:[{type:"stat_bonus",target:"melee_damage",value:2,condition:"while raging"}],source:"default",tags:["combat","damage"]},{id:"barbarian_unarmored_defense",name:"Unarmored Defense",description:"While you are not wearing any armor, your Armor Class equals 10 + your Dexterity modifier + your Constitution modifier.",type:"passive",level:1,class:"Barbarian",source:"default",tags:["defense"]},{id:"barbarian_reckless_attack",name:"Reckless Attack",description:"You can throw aside all concern for defense to attack with fierce desperation. When you make your first attack on your turn, you can decide to attack recklessly. Doing so gives you advantage on melee weapon attack rolls using Strength during this turn, but attack rolls against you have advantage until your next turn.",type:"active",level:2,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_danger_sense",name:"Danger Sense",description:"You gain an uncanny sense of when things nearby aren't as they should be, giving you edge when you attempt to dodge a danger or react to a hidden threat. You have advantage on Dexterity saving throws against effects that you can see, such as traps and spells.",type:"passive",level:2,class:"Barbarian",source:"default",tags:["defense","perception"]},{id:"barbarian_extra_attack",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_fast_movement",name:"Fast Movement",description:"Your speed increases by 10 feet while you aren't wearing heavy armor.",type:"passive",level:5,class:"Barbarian",effects:[{type:"passive_modifier",target:"speed",value:10,condition:"unarmored"}],source:"default",tags:["movement"]},{id:"barbarian_feral_instinct",name:"Feral Instinct",description:"Your instincts are so honed that you have advantage on initiative rolls. Additionally, if you are surprised at the start of combat and aren't incapacitated, you can act normally on your first turn.",type:"passive",level:7,class:"Barbarian",source:"default",tags:["combat","perception"]},{id:"barbarian_brutal_critical",name:"Brutal Critical",description:"When you score a critical hit with a melee weapon attack, you can roll one of the weapon's damage dice one additional time and add it to the extra damage of the critical hit.",type:"passive",level:9,class:"Barbarian",source:"default",tags:["combat","damage"]},{id:"barbarian_relentless_endurance",name:"Relentless Endurance",description:"When you are reduced to 0 hit points but not killed outright, you can drop to 1 hit point instead. You can't use this feature again until you finish a long rest.",type:"active",level:11,class:"Barbarian",source:"default",tags:["survival"]},{id:"barbarian_persistent_rage",name:"Persistent Rage",description:"Your rage can't end early due to fatigue.",type:"passive",level:11,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_indomitable_might",name:"Indomitable Might",description:"Your Strength score increases by 4, and your maximum for that score becomes 24.",type:"passive",level:18,class:"Barbarian",effects:[{type:"stat_bonus",target:"STR",value:4},{type:"passive_modifier",target:"STR_max",value:24}],source:"default",tags:["ability"]},{id:"barbarian_primal_champion",name:"Primal Champion",description:"Your Strength and Constitution scores increase by 4, and your maximum for those scores becomes 24.",type:"passive",level:20,class:"Barbarian",effects:[{type:"stat_bonus",target:"STR",value:4},{type:"stat_bonus",target:"CON",value:4},{type:"passive_modifier",target:"STR_max",value:24},{type:"passive_modifier",target:"CON_max",value:24}],source:"default",tags:["ability"]},{id:"bardic_inspiration",name:"Bardic Inspiration",description:"You can inspire others through stirring words or music. To do so, you use a bonus action on your turn to choose one creature other than yourself within 60 feet of you who can hear you. That creature gains one Bardic Inspiration die, a d6.",type:"resource",level:1,class:"Bard",source:"default",tags:["support","resource"]},{id:"jack_of_all_trades",name:"Jack of All Trades",description:"You can add half your proficiency bonus, rounded down, to any ability check you make that doesn't already include your proficiency bonus.",type:"passive",level:2,class:"Bard",source:"default",tags:["skill"]},{id:"song_of_rest",name:"Song of Rest",description:"You can use soothing music or oration to help revitalize your wounded allies during a short rest. If you or any friendly creatures who can hear your performance regain hit points at the end of the short rest, each of those creatures regains an extra 1d6 hit points.",type:"passive",level:2,class:"Bard",source:"default",tags:["healing","support"]},{id:"bard_college",name:"Bard College",description:"At 3rd level, you delve into the advanced techniques of a bard college of your choice. Your choice grants you features at 3rd level and again at 6th and 14th level.",type:"passive",level:3,class:"Bard",source:"default",tags:["subclass"]},{id:"bardic_versatility",name:"Bardic Versatility",description:"Whenever you reach a level in this class that grants the Ability Score Improvement feature, you can do one of the following: Replace one of your skills with another skill from the bard list, gain proficiency in one skill of your choice, gain proficiency with one musical instrument of your choice, or gain proficiency with one tool of your choice.",type:"passive",level:4,class:"Bard",source:"default",tags:["skill","proficiency"]},{id:"font_of_inspiration",name:"Font of Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain one use.",type:"passive",level:5,class:"Bard",source:"default",tags:["resource"]},{id:"countercharm",name:"Countercharm",description:"You gain the ability to use musical notes or words of power to disrupt mind-influencing effects. As an action, you can start a performance that lasts until the end of your next turn. During that time, you and any friendly creatures within 30 feet of you have advantage on saving throws against being frightened or charmed.",type:"active",level:6,class:"Bard",source:"default",tags:["support","defense"]},{id:"superior_inspiration",name:"Superior Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain two uses.",type:"passive",level:10,class:"Bard",source:"default",tags:["resource"]},{id:"magical_secrets",name:"Magical Secrets",description:"You learn two spells of your choice from any class. A spell you choose must be of a level you can cast, as shown on the Bard table, or a cantrip. The chosen spells count as bard spells for you.",type:"passive",level:10,class:"Bard",source:"default",tags:["magic"]},{id:"unforgettable_face",name:"Unforgettable Face",description:"When you speak to a creature for the first time, you can choose one creature. That creature has disadvantage on attack rolls against you for 1 minute.",type:"active",level:14,class:"Bard",source:"default",tags:["social","defense"]},{id:"magical_secrets_2",name:"Magical Secrets",description:"You learn two spells of your choice from any class.",type:"passive",level:14,class:"Bard",source:"default",tags:["magic"]},{id:"bardic_inspiration_d8",name:"Bardic Inspiration (d8)",description:"Your Bardic Inspiration die changes to a d8.",type:"passive",level:15,class:"Bard",source:"default",tags:["resource"]},{id:"magical_secrets_3",name:"Magical Secrets",description:"You learn two spells of your choice from any class.",type:"passive",level:18,class:"Bard",source:"default",tags:["magic"]},{id:"bardic_inspiration_d10",name:"Bardic Inspiration (d10)",description:"Your Bardic Inspiration die changes to a d10.",type:"passive",level:20,class:"Bard",source:"default",tags:["resource"]},{id:"superior_inspiration_3",name:"Superior Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain three uses.",type:"passive",level:20,class:"Bard",source:"default",tags:["resource"]},{id:"divine_domain",name:"Divine Domain",description:"Choose one domain related to your deity: Knowledge, Life, Light, Nature, Tempest, Trickery, or War. Each domain grants domain spells and other features at levels 1, 2, 6, 8, and 17.",type:"passive",level:1,class:"Cleric",source:"default",tags:["subclass","magic"]},{id:"divine_intervention",name:"Divine Intervention",description:"You can call on your deity to intervene on your behalf. Your deity intervenes by causing a effect related to their portfolio. You can use this feature once between long rests.",type:"active",level:10,class:"Cleric",source:"default",tags:["magic","divine"]},{id:"destroy_undead",name:"Destroy Undead",description:"When an undead fails its saving throw against your Turn Undead feature, the creature is instantly destroyed if its challenge rating is at or below a threshold shown on the Cleric table.",type:"passive",level:5,class:"Cleric",source:"default",tags:["combat","divine"]},{id:"divine_intervention_improved",name:"Divine Intervention (Improved)",description:"You can use Divine Intervention twice between long rests.",type:"active",level:20,class:"Cleric",source:"default",tags:["magic","divine"]},{id:"druidic",name:"Druidic",description:"You know Druidic, the secret language of druids. You can speak the language and use it to leave hidden messages. You and others who know this language automatically spot such a message. Others spot the message's presence with a successful DC 15 Wisdom (Perception) check but can't decipher it without magic.",type:"passive",level:1,class:"Druid",source:"default",tags:["language","secret"]},{id:"wild_shape",name:"Wild Shape",description:"You can use your action to magically assume the shape of a beast that you have seen before. You can transform into a beast with a challenge rating as shown on the Druid table.",type:"active",level:2,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_combat",name:"Wild Shape (Combat)",description:"You can transform into a beast with a challenge rating up to 1.",type:"passive",level:2,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"druid_circle",name:"Druid Circle",description:"At 2nd level, you choose to identify with a circle of druids. Your choice grants you features at 2nd level and again at 6th, 10th, and 14th level.",type:"passive",level:2,class:"Druid",source:"default",tags:["subclass"]},{id:"wild_shape_aquatic",name:"Wild Shape (Aquatic)",description:"You can transform into a beast with a swimming speed.",type:"passive",level:4,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_cr_2",name:"Wild Shape (CR 2)",description:"You can transform into a beast with a challenge rating up to 2.",type:"passive",level:8,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_cr_3",name:"Wild Shape (CR 3)",description:"You can transform into a beast with a challenge rating up to 3.",type:"passive",level:8,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"timeless_body",name:"Timeless Body",description:"You age slowly, appearing no older than you did at the start of your druidic studies.",type:"passive",level:18,class:"Druid",source:"default",tags:["ability"]},{id:"beast_spells",name:"Beast Spells",description:"You can cast many of your druid spells in any shape you assume using Wild Shape.",type:"passive",level:18,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"archdruid",name:"Archdruid",description:"You can use your Wild Shape an unlimited number of times.",type:"passive",level:20,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"fighting_style",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Archery, Defense, Dueling, Great Weapon Fighting, Protection, Two-Weapon Fighting, or Unarmed Fighting.",type:"passive",level:1,class:"Fighter",source:"default",tags:["combat","style"]},{id:"second_wind",name:"Second Wind",description:"You have a limited well of stamina you can draw on to protect yourself. As a bonus action, you can regain hit points equal to 1d10 + your fighter level.",type:"active",level:1,class:"Fighter",source:"default",tags:["healing","combat"]},{id:"action_surge",name:"Action Surge",description:"You can push yourself beyond your normal limits for a moment. On your turn, you can take one additional action.",type:"active",level:2,class:"Fighter",source:"default",tags:["combat"]},{id:"martial_archetype",name:"Martial Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles. Your choice grants you features at 3rd level and again at 7th, 10th, 15th, and 18th level.",type:"passive",level:3,class:"Fighter",source:"default",tags:["subclass"]},{id:"fighter_extra_attack",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Fighter",source:"default",tags:["combat"]},{id:"indomitable",name:"Indomitable",description:"You can reroll a saving throw that you fail. If you do so, you must use the new roll.",type:"active",level:9,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"indomitable_2",name:"Indomitable (Twice)",description:"You can use Indomitable twice between long rests.",type:"active",level:13,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"indomitable_3",name:"Indomitable (Thrice)",description:"You can use Indomitable three times between long rests.",type:"active",level:17,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"action_surge_2",name:"Action Surge (Improved)",description:"You can use Action Surge twice between short rests.",type:"passive",level:17,class:"Fighter",source:"default",tags:["combat"]},{id:"unarmored_defense_monk",name:"Unarmored Defense",description:"Beginning at 1st level, while you are wearing no armor and not wielding a shield, your AC equals 10 + your Dexterity modifier + your Wisdom modifier.",type:"passive",level:1,class:"Monk",source:"default",tags:["defense"]},{id:"martial_arts",name:"Martial Arts",description:"You gain the following benefits while you are unarmed or wielding only monk weapons and you aren't wearing armor or wielding a shield: You can use Dexterity instead of Strength for attacks and damage rolls with your unarmed strikes and monk weapons, you can roll a d4 in place of the normal damage of your monk weapon or unarmed strike, and when you use the Attack action with an unarmed strike or a monk weapon, you can make one unarmed strike as a bonus action.",type:"passive",level:1,class:"Monk",source:"default",tags:["combat"]},{id:"ki",name:"Ki",description:"Starting at 2nd level, your training allows you to harness the mystic energy of ki. Your access to this energy is represented by a number of Ki points. You have a number of Ki points equal to your monk level.",type:"resource",level:2,class:"Monk",source:"default",tags:["resource","magic"]},{id:"flurry_of_blows",name:"Flurry of Blows",description:"Immediately after you take the Attack action on your turn, you can spend 1 Ki point to make two unarmed strikes as a bonus action.",type:"active",level:2,class:"Monk",source:"default",tags:["combat"]},{id:"patient_defense",name:"Patient Defense",description:"You can spend 1 Ki point to take the Dodge action as a bonus action on your turn.",type:"active",level:2,class:"Monk",source:"default",tags:["defense","combat"]},{id:"step_of_the_wind",name:"Step of the Wind",description:"You can spend 1 Ki point to take the Disengage or Dash action as a bonus action on your turn, and your jump distance is doubled for the turn.",type:"active",level:2,class:"Monk",source:"default",tags:["movement","combat"]},{id:"monastic_tradition",name:"Monastic Tradition",description:"When you reach 3rd level, you commit yourself to a monastic tradition. Your tradition grants you features at 3rd level and again at 6th, 11th, and 17th level.",type:"passive",level:3,class:"Monk",source:"default",tags:["subclass"]},{id:"slow_fall",name:"Slow Fall",description:"At 4th level, you can use your reaction when you fall to reduce any falling damage you take by an amount equal to five times your monk level.",type:"passive",level:4,class:"Monk",source:"default",tags:["defense","movement"]},{id:"extra_attack_monk",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Monk",source:"default",tags:["combat"]},{id:"stunning_strike",name:"Stunning Strike",description:"Starting at 5th level, you can interfere with the flow of ki in an opponent's body. When you hit another creature with a melee weapon attack, you can spend 1 Ki point to attempt a stunning strike. The target must succeed on a Constitution saving throw or be stunned until the end of your next turn.",type:"active",level:5,class:"Monk",source:"default",tags:["combat"]},{id:"ki_empowered_strikes",name:"Ki-Empowered Strikes",description:"Starting at 6th level, your unarmed strikes count as magical for the purpose of overcoming resistance and immunity to nonmagical attacks and damage.",type:"passive",level:6,class:"Monk",source:"default",tags:["combat","magic"]},{id:"stillness_of_mind",name:"Stillness of Mind",description:"At 7th level, you can use your action to end one effect on yourself that is causing you to be charmed or frightened.",type:"active",level:7,class:"Monk",source:"default",tags:["defense"]},{id:"unarmored_movement",name:"Unarmored Movement",description:"Your speed increases by 10 feet while you are not wearing armor or wielding a shield. This bonus increases when you reach certain monk levels.",type:"passive",level:2,class:"Monk",effects:[{type:"passive_modifier",target:"speed",value:10,condition:"unarmored"}],source:"default",tags:["movement"]},{id:"pure_body",name:"Purity of Body",description:"At 10th level, your mastery of the ki flowing through you makes you immune to disease and poison.",type:"passive",level:10,class:"Monk",source:"default",tags:["defense","ability"]},{id:"tongue_of_the_sun_and_moon",name:"Tongue of the Sun and Moon",description:"At 13th level, you learn to touch the ki of other minds so that you understand all spoken languages. Moreover, any creature that can understand a language can understand what you say.",type:"passive",level:13,class:"Monk",source:"default",tags:["language","social"]},{id:"empty_body",name:"Empty Body",description:"At 18th level, you can spend 4 Ki points to start becoming invisible for 1 minute. During that time, you also have resistance to all damage but force damage.",type:"active",level:18,class:"Monk",source:"default",tags:["defense","magic"]},{id:"divine_sense",name:"Divine Sense",description:"As an action, you can detect good and evil. You can sense the presence of any celestial, fiend, or undead within 60 feet of you.",type:"active",level:1,class:"Paladin",source:"default",tags:["perception","divine"]},{id:"lay_on_hands",name:"Lay on Hands",description:"Your blessed touch can heal wounds. You have a pool of healing power that replenishes when you take a long rest. With that pool, you can restore a total number of hit points equal to your paladin level × 5.",type:"active",level:2,class:"Paladin",source:"default",tags:["healing","divine"]},{id:"fighting_style_paladin",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Defense, Dueling, Great Weapon Fighting, Protection, or Two-Weapon Fighting.",type:"passive",level:2,class:"Paladin",source:"default",tags:["combat","style"]},{id:"divine_smite",name:"Divine Smite",description:"When you hit a creature with a melee weapon attack, you can expend one spell slot to deal radiant damage to the target.",type:"active",level:2,class:"Paladin",source:"default",tags:["combat","divine"]},{id:"sacred_oath",name:"Sacred Oath",description:"When you reach 3rd level, you swear the oath that binds you as a paladin forever. Your choice grants you features at 3rd level and again at 7th, 15th, and 20th level.",type:"passive",level:3,class:"Paladin",source:"default",tags:["subclass","divine"]},{id:"aura_of_protection",name:"Aura of Protection",description:"Starting at 6th level, you and friendly creatures within 10 feet of you gain a bonus to all saving throws equal to your Charisma modifier (minimum of +1).",type:"passive",level:6,class:"Paladin",source:"default",tags:["defense","aura"]},{id:"aura_of_courage",name:"Aura of Courage",description:"Starting at 10th level, you and friendly creatures within 10 feet of you can't be frightened.",type:"passive",level:10,class:"Paladin",source:"default",tags:["defense","aura"]},{id:"improved_divine_smite",name:"Improved Divine Smite",description:"Starting at 11th level, you are so suffused with righteous might that all your melee weapon strikes carry divine power with them. Whenever you hit a creature with a melee weapon, the creature takes an extra 1d8 radiant damage.",type:"passive",level:11,class:"Paladin",source:"default",tags:["combat","divine"]},{id:"cleansing_touch",name:"Cleansing Touch",description:"At 14th level, you can use your action to end one spell or effect on yourself or one willing creature that you touch.",type:"active",level:14,class:"Paladin",source:"default",tags:["defense","divine"]},{id:"aura_improvements",name:"Aura Improvements",description:"At 18th level, the range of your auras increases to 30 feet.",type:"passive",level:18,class:"Paladin",source:"default",tags:["aura"]},{id:"favored_enemy",name:"Favored Enemy",description:"You have significant experience studying, tracking, hunting, and even talking to a certain type of enemy. Choose a type of favored enemy: aberrations, beasts, celestials, constructs, dragons, elementals, fey, fiends, giants, monstrosities, oozes, plants, or undead. You gain a bonus to damage rolls against creatures of the chosen type.",type:"passive",level:1,class:"Ranger",source:"default",tags:["combat"]},{id:"natural_explorer",name:"Natural Explorer",description:"You are a master of navigating the natural world. You choose a type of favored terrain: arctic, coast, desert, forest, grassland, mountain, swamp, or the Underdark. While traveling for an hour or more in your favored terrain, you gain the following benefits: Difficult terrain doesn't slow your group's travel, your group can't become lost except by magical means, and you remain alert to danger even when you are engaged in another activity.",type:"passive",level:1,class:"Ranger",source:"default",tags:["exploration"]},{id:"fighting_style_ranger",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Archery, Defense, Dueling, Great Weapon Fighting, Two-Weapon Fighting, or Mariner (if you have a swimming speed).",type:"passive",level:2,class:"Ranger",source:"default",tags:["combat","style"]},{id:"spellcasting_ranger",name:"Spellcasting",description:"By the time you reach 2nd level, you have learned to use the magical essence of nature to cast spells, much as a druid does.",type:"passive",level:2,class:"Ranger",source:"default",tags:["magic"]},{id:"ranger_archetype",name:"Ranger Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles and techniques. Your choice grants you features at 3rd level and again at 5th, 7th, 11th, and 15th level.",type:"passive",level:3,class:"Ranger",source:"default",tags:["subclass"]},{id:"prime_awareness",name:"Prime-Aware Explorer",description:"At 6th level, your mastery of the wild allows you to navigate with ease. You can use the Hide action as a bonus action. You also gain an additional Favored Enemy and Favored Terrain.",type:"passive",level:6,class:"Ranger",source:"default",tags:["exploration","stealth"]},{id:"fleet_of_foot",name:"Fleet of Foot",description:"Your speed increases by 5 feet.",type:"passive",level:8,class:"Ranger",effects:[{type:"passive_modifier",target:"speed",value:5}],source:"default",tags:["movement"]},{id:"hide_in_plain_sight",name:"Hide in Plain Sight",description:"Starting at 10th level, you can remain perfectly still for long periods to blend in with your surroundings. You can spend 1 minute creating camouflage and gain advantage on Stealth checks.",type:"active",level:10,class:"Ranger",source:"default",tags:["stealth"]},{id:"vanish",name:"Vanish",description:"Starting at 14th level, you can use the Hide action as a bonus action on your turn. Also, you can't be tracked by nonmagical means.",type:"passive",level:14,class:"Ranger",source:"default",tags:["stealth"]},{id:"feral_senses",name:"Feral Senses",description:"At 18th level, you gain preternatural senses that help you fight creatures you can't see. When you attack a creature you can't see, your inability to see it doesn't impose disadvantage on your attack rolls. You are also aware of the location of any invisible creature within 30 feet of you.",type:"passive",level:18,class:"Ranger",source:"default",tags:["perception","combat"]},{id:"sneak_attack",name:"Sneak Attack",description:"You know how to strike subtly and exploit a foe's distraction. Once per turn, you can deal an extra 1d6 damage to one creature you hit with an attack if you have advantage on the attack roll.",type:"passive",level:1,class:"Rogue",source:"default",tags:["combat","damage"]},{id:"thieves_cant",name:"Thieves' Cant",description:"During your rogue training you learned thieves' cant, a secret mix of dialect, hand signs, and body language that allows you to hide messages in seemingly normal conversation.",type:"passive",level:1,class:"Rogue",source:"default",tags:["language","secret"]},{id:"cunning_action",name:"Cunning Action",description:"You can take a bonus action on each of your turns in combat. This action can be used only to take the Dash, Disengage, or Hide action.",type:"active",level:2,class:"Rogue",source:"default",tags:["combat","movement"]},{id:"roguish_archetype",name:"Roguish Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles and techniques. Your choice grants you features at 3rd level and then again at 9th, 13th, and 17th level.",type:"passive",level:3,class:"Rogue",source:"default",tags:["subclass"]},{id:"ability_score_improvement_rogue",name:"Ability Score Improvement",description:"At 4th level, you can increase one ability score by 2 or two ability scores by 1.",type:"passive",level:4,class:"Rogue",source:"default",tags:["ability"]},{id:"uncanny_dodge",name:"Uncanny Dodge",description:"Starting at 5th level, when an attacker that you can see hits you with an attack, you can use your reaction to halve the attack's damage against you.",type:"active",level:5,class:"Rogue",source:"default",tags:["defense","combat"]},{id:"evasion",name:"Evasion",description:"Starting at 7th level, you can nimbly dodge out of the way of certain area effects. When you are subjected to an effect that allows you to make a Dexterity saving throw to take only half damage, you instead take no damage if you succeed and only half damage if you fail.",type:"passive",level:7,class:"Rogue",source:"default",tags:["defense"]},{id:"slippery_mind",name:"Slippery Mind",description:"At 11th level, you have acquired greater mental strength. You gain proficiency in Wisdom saving throws.",type:"passive",level:11,class:"Rogue",source:"default",tags:["defense"]},{id:"elusive",name:"Elusive",description:"Beginning at 18th level, you are so evasive that attackers rarely gain the upper hand against you. No attack roll has advantage against you while you aren't incapacitated.",type:"passive",level:18,class:"Rogue",source:"default",tags:["defense"]},{id:"stroke_of_luck",name:"Stroke of Luck",description:"At 20th level, you have an uncanny knack for succeeding when you need to. If your attack misses a target within range, you can turn the miss into a hit. Alternatively, if you fail an ability check, you can treat the d20 roll as a 20.",type:"active",level:20,class:"Rogue",source:"default",tags:["combat","luck"]},{id:"sorcerous_restoration",name:"Sorcerous Restoration",description:"You can regain 4 sorcery points on a short rest.",type:"passive",level:20,class:"Sorcerer",source:"default",tags:["resource"]},{id:"font_of_magic",name:"Font of Magic",description:"You can manipulate the weave of magic to your advantage. You have 2 sorcery points. You gain more as you level. You can use sorcery points to create additional spell slots or gain spell slots.",type:"resource",level:2,class:"Sorcerer",source:"default",tags:["resource","magic"]},{id:"metamagic",name:"Metamagic",description:"At 3rd level, you gain the ability to twist your spells to suit your needs. You gain two metamagic options of your choice: Careful Spell, Distant Spell, Empowered Spell, Extended Spell, Heightened Spell, Quickened Spell, Subtle Spell, Twinned Spell.",type:"active",level:3,class:"Sorcerer",source:"default",tags:["magic"]},{id:"sorcerous_origin",name:"Sorcerous Origin",description:"Choose a sorcerous origin, which describes the source of your magical power: Draconic Bloodline, Wild Magic, or another origin.",type:"passive",level:1,class:"Sorcerer",source:"default",tags:["subclass"]},{id:"metamagic_3",name:"Metamagic (3 options)",description:"At 10th level, you gain a third metamagic option.",type:"active",level:10,class:"Sorcerer",source:"default",tags:["magic"]},{id:"metamagic_4",name:"Metamagic (4 options)",description:"At 17th level, you gain a fourth metamagic option.",type:"active",level:17,class:"Sorcerer",source:"default",tags:["magic"]},{id:"otherworldly_patron",name:"Otherworldly Patron",description:"At 1st level, you have struck a bargain with an otherworldly being of power. Your choice grants you features at 1st level and again at 6th, 10th, and 14th level.",type:"passive",level:1,class:"Warlock",source:"default",tags:["subclass"]},{id:"pact_magic",name:"Pact Magic",description:"You have learned to delve into the secrets of magic to harness the power of your patron. You know two cantrips and a number of spells.",type:"passive",level:1,class:"Warlock",source:"default",tags:["magic"]},{id:"eldritch_master",name:"Eldritch Master",description:"At 20th level, you can draw on your inner reserve of magical power while entreating your patron to regain expended spell slots. You can regain all your warlock spell slots on a short or long rest.",type:"active",level:20,class:"Warlock",source:"default",tags:["magic","resource"]},{id:"mystic_arcanum",name:"Mystic Arcanum",description:"At 11th level, your patron bestows upon you the ability to cast a 6th-level spell. You gain additional spells at higher levels.",type:"passive",level:11,class:"Warlock",source:"default",tags:["magic"]},{id:"arcane_recovery",name:"Arcane Recovery",description:"You have learned to regain some of your magical energy by studying your spellbook. Once per day when you finish a short rest, you can recover expended spell slots whose combined level equals no more than half your wizard level.",type:"active",level:1,class:"Wizard",source:"default",tags:["magic","resource"]},{id:"arcane_tradition",name:"Arcane Tradition",description:"When you reach 2nd level, you choose an arcane tradition that shapes your practice of magic. Your choice grants you features at 2nd level and again at 6th, 10th, and 14th level.",type:"passive",level:2,class:"Wizard",source:"default",tags:["subclass"]},{id:"spell_mastery",name:"Spell Mastery",description:"At 18th level, you have achieved profound mastery over spells. Choose two 1st-level wizard spells that you know. You can cast these spells at their lowest level without expending a spell slot.",type:"passive",level:18,class:"Wizard",source:"default",tags:["magic"]},{id:"signature_spells",name:"Signature Spells",description:"When you reach 20th level, you gain mastery over two powerful spells. Choose two 3rd-level wizard spells that you know. You can cast these spells at their lowest level without expending a spell slot.",type:"passive",level:20,class:"Wizard",source:"default",tags:["magic"]}],fa=[{id:"human_versatile",name:"Versatile",description:"Humans gain proficiency in one skill of their choice.",race:"Human",source:"default",tags:["skill"]},{id:"human_extra_language",name:"Extra Language",description:"Humans can speak, read, and write one extra language of their choice.",race:"Human",source:"default",tags:["language"]},{id:"elf_darkvision",name:"Darkvision",description:"Accustomed to twilit forests and the night sky, elves have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Elf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"elf_keen_senses",name:"Keen Senses",description:"Elves have proficiency in the Perception skill.",race:"Elf",source:"default",effects:[{type:"skill_proficiency",target:"perception",value:"proficient"}],tags:["skill","perception"]},{id:"elf_fey_ancestry",name:"Fey Ancestry",description:"Elves have advantage on saving throws against being charmed, and magic can't put them to sleep.",race:"Elf",source:"default",tags:["defense","magic"]},{id:"elf_trance",name:"Trance",description:"Elves don't need to sleep. Instead, they meditate deeply, remaining semiconscious, for 4 hours a day. After resting in this way, you gain the same benefit that a human does from 8 hours of sleep.",race:"Elf",source:"default",tags:["ability"]},{id:"dwarf_darkvision",name:"Darkvision",description:"Accustomed to life underground, dwarves have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Dwarf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"dwarf_dwarven_resilience",name:"Dwarven Resilience",description:"Dwarves have advantage on saving throws against poison, and resistance against poison damage.",race:"Dwarf",source:"default",tags:["defense"]},{id:"dwarf_stonecunning",name:"Stonecunning",description:"Whenever you make an Intelligence (History) check related to the origin of stonework, you are considered proficient in the History skill and add double your proficiency bonus to the check.",race:"Dwarf",source:"default",tags:["skill","exploration"]},{id:"halfling_lucky",name:"Lucky",description:"When you roll a 1 on an attack roll, ability check, or saving throw, you can reroll the die and must use the new roll.",race:"Halfling",source:"default",tags:["luck"]},{id:"halfling_brave",name:"Brave",description:"Halflings have advantage on saving throws against being frightened.",race:"Halfling",source:"default",tags:["defense"]},{id:"halfling_halfling_nimbleness",name:"Halfling Nimbleness",description:"Halflings can move through the space of any creature that is larger than them.",race:"Halfling",source:"default",tags:["movement"]},{id:"dragonborn_draconic_ancestry",name:"Draconic Ancestry",description:"Dragonborn have draconic ancestry linked to a specific type of dragon. This determines their breath weapon and damage resistance.",race:"Dragonborn",source:"default",tags:["ancestry","combat"]},{id:"dragonborn_breath_weapon",name:"Breath Weapon",description:"Dragonborn can use their action to exhale destructive energy. Their draconic ancestry determines the size, shape, and damage type of the breath weapon.",race:"Dragonborn",source:"default",tags:["combat"]},{id:"dragonborn_damage_resistance",name:"Damage Resistance",description:"Dragonborn have resistance to the damage type associated with their draconic ancestry.",race:"Dragonborn",source:"default",tags:["defense"]},{id:"gnome_darkvision",name:"Darkvision",description:"Accustomed to life underground, gnomes have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Gnome",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"gnome_gnome_cunning",name:"Gnome Cunning",description:"Gnomes have advantage on all Intelligence, Wisdom, and Charisma saving throws against magic.",race:"Gnome",source:"default",tags:["defense","magic"]},{id:"half_elf_darkvision",name:"Darkvision",description:"Thanks to your elf blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Half-Elf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"half_elf_fey_ancestry",name:"Fey Ancestry",description:"Half-elves have advantage on saving throws against being charmed, and magic can't put them to sleep.",race:"Half-Elf",source:"default",tags:["defense","magic"]},{id:"half_elf_skill_versatility",name:"Skill Versatility",description:"Half-elves gain proficiency in two skills of their choice.",race:"Half-Elf",source:"default",tags:["skill"]},{id:"half_orc_darkvision",name:"Darkvision",description:"Thanks to your orc blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Half-Orc",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"half_orc_relentless_endurance",name:"Relentless Endurance",description:"When you are reduced to 0 hit points but not killed outright, you can drop to 1 hit point instead. You can't use this feature again until you finish a long rest.",race:"Half-Orc",source:"default",tags:["survival"]},{id:"half_orc_savage_attacks",name:"Savage Attacks",description:"When you score a critical hit with a melee weapon attack, you can roll one of the weapon's damage dice one additional time and add it to the extra damage of the critical hit.",race:"Half-Orc",source:"default",tags:["combat","damage"]},{id:"tiefling_darkvision",name:"Darkvision",description:"Thanks to your infernal heritage, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Tiefling",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"tiefling_hellish_resistance",name:"Hellish Resistance",description:"Tieflings have resistance to fire damage.",race:"Tiefling",source:"default",tags:["defense"]},{id:"tiefling_infernal_legacy",name:"Infernal Legacy",description:"Tieflings know the thaumaturgy cantrip. When you reach 3rd level, you can cast the hellish rebuke spell as a 2nd-level spell once with this trait and regain the ability to do so when you finish a long rest. When you reach 5th level, you can cast the darkness spell once with this trait and regain the ability to do so when you finish a long rest. Charisma is your spellcasting ability for these spells.",race:"Tiefling",source:"default",tags:["magic","spell"]}],ya=[{id:"athletics",name:"Athletics",ability:"STR",armorPenalty:!0,categories:["physical","exploration","combat"],source:"default",description:"Covers climbing, jumping, swimming, and other physical feats of strength. Use this skill when you need to scale walls, leap across chasms, swim in turbulent waters, or perform any other physical activity that raw strength can accomplish."},{id:"acrobatics",name:"Acrobatics",ability:"DEX",armorPenalty:!0,categories:["physical","exploration"],source:"default",description:"Covers balancing, tumbling, and maintaining footing in difficult situations. Use this skill to walk on tightropes, stay on your feet during earthquakes, perform gymnastic feats, and dodge falling objects or area effects."},{id:"sleight_of_hand",name:"Sleight of Hand",ability:"DEX",armorPenalty:!0,categories:["physical","criminal","subterfuge"],source:"default",description:"Covers manual dexterity and fine motor skills such as picking pockets, palming items, and performing magic tricks. Use this skill when you need to steal something unnoticed, hide an object on your person, or manipulate small objects with precision."},{id:"stealth",name:"Stealth",ability:"DEX",armorPenalty:!0,categories:["physical","subterfuge","exploration"],source:"default",description:"Covers hiding, moving silently, and avoiding detection. Use this skill when you want to sneak past guards, hide in shadows, stalk prey, or conceal yourself from enemies. Your environment matters—darkness and cover provide advantage."},{id:"arcana",name:"Arcana",ability:"INT",armorPenalty:!1,categories:["knowledge","magic"],source:"default",description:"Covers knowledge about magical theory, arcane traditions, magical creatures, and the planes. Use this skill to identify magical items, understand spells, recognize arcane symbols, and recall lore about wizards, dragons, and other magical phenomena."},{id:"history",name:"History",ability:"INT",armorPenalty:!1,categories:["knowledge"],source:"default",description:"Covers knowledge about historical events, legendary figures, ancient civilizations, and past conflicts. Use this skill to recall the origins of ruins, identify royal lineages, understand the significance of ancient documents, and recognize historical artifacts."},{id:"investigation",name:"Investigation",ability:"INT",armorPenalty:!1,categories:["knowledge","exploration"],source:"default",description:"Covers deductive reasoning, analyzing clues, and piecing together information. Use this skill when you need to deduce the composition of an object, follow tracks, interpret coded messages, or draw conclusions from scattered evidence."},{id:"nature",name:"Nature",ability:"INT",armorPenalty:!1,categories:["knowledge","environmental"],source:"default",description:"Covers knowledge about plants, animals, weather, and the natural world. Use this skill to identify plants and beasts, predict weather changes, understand terrain, and recall lore about the wilderness and its inhabitants."},{id:"religion",name:"Religion",ability:"INT",armorPenalty:!1,categories:["knowledge"],source:"default",description:"Covers knowledge about deities, religious traditions, divine magic, and the planes of existence. Use this skill to recognize religious symbols, understand divine spells, identify outsiders, and recall lore about gods, angels, and religious cults."},{id:"animal_handling",name:"Animal Handling",ability:"WIS",armorPenalty:!1,categories:["environmental","social"],source:"default",description:"Covers calming, training, and understanding animals. Use this skill when you need to control a mount, calm a frightened beast, read an animal's mood, or predict how an animal might react. This skill doesn't work on creatures with Intelligence higher than 2."},{id:"insight",name:"Insight",ability:"WIS",armorPenalty:!1,categories:["social","perception"],source:"default",description:"Covers reading people, detecting lies, and understanding motives. Use this skill to determine when someone is lying to you, sense hidden agendas, gauge someone's true feelings, or predict how someone might act in a social situation."},{id:"medicine",name:"Medicine",ability:"WIS",armorPenalty:!1,categories:["knowledge","practical"],source:"default",description:"Covers diagnosing illnesses, treating wounds, and stabilizing dying creatures. Use this skill to determine the cause of a strange illness, assess the severity of injuries, provide first aid, or stabilize a dying companion. This skill does not cover magical healing."},{id:"perception",name:"Perception",ability:"WIS",armorPenalty:!1,categories:["perception","exploration"],source:"default",description:"Covers awareness of your surroundings and noticing hidden things. Use this skill to spot hidden enemies, find secret doors, hear approaching creatures, detect ambushes, or notice unusual details in your environment. This is the most commonly used skill in the game."},{id:"survival",name:"Survival",ability:"WIS",armorPenalty:!1,categories:["environmental","exploration"],source:"default",description:"Covers following tracks, hunting, foraging, and navigating in the wilderness. Use this skill to follow creature tracks, find food and water, predict weather, navigate without a map, or avoid natural hazards. This skill is essential for wilderness adventures."},{id:"deception",name:"Deception",ability:"CHA",armorPenalty:!1,categories:["social","subterfuge"],source:"default",description:"Covers lying, misdirection, and hiding the truth. Use this skill when you need to convincingly tell a falsehood, maintain a cover identity, pass hidden messages, or trick someone into believing something that isn't true. Success depends on plausibility and your target's suspicions."},{id:"intimidation",name:"Intimidation",ability:"CHA",armorPenalty:!1,categories:["social","combat"],source:"default",description:"Covers frightening others and forcing compliance through threats. Use this skill when you want to scare someone into backing down, extract information through fear, force a prisoner to talk, or make an opponent hesitate. Physical presence and reputation can provide advantage."},{id:"performance",name:"Performance",ability:"CHA",armorPenalty:!1,categories:["social","entertainment"],source:"default",description:"Covers entertaining audiences through music, acting, storytelling, or other arts. Use this skill when you need to captivate an audience, earn money through performance, distract guards with entertainment, or pass as a traveling performer. Quality and audience receptiveness affect the DC."},{id:"persuasion",name:"Persuasion",ability:"CHA",armorPenalty:!1,categories:["social"],source:"default",description:"Covers influencing others through reason, charm, and good faith. Use this skill when you need to convince someone to see your point of view, negotiate favorable terms, get someone to do you a favor, or rally people to your cause. This skill works best when you're being honest."}],ch=["physical","exploration","combat","criminal","subterfuge","knowledge","magic","environmental","social","perception","practical","entertainment"],uh={"appearance.bodyTypes":["slender","athletic","muscular","stocky"],"appearance.skinTones":["#F5E6D3","#E8C4A0","#D4A574","#C68642","#8D5524","#5C3317"],"appearance.hairColors":["#1C1C1C","#3B2414","#6A4E23","#A67B5B","#D4AF37","#E9C2A6","#B55239","#DC143C","#C0C0C0","#FFFFFF"],"appearance.hairStyles":["short","long","bald","braided","curly","wavy","straight","ponytail","mohawk","dreadlocks"],"appearance.eyeColors":["#3B2414","#6F4E37","#228B22","#4169E1","#708090","#000000"],"appearance.facialFeatures":["scar on cheek","tattoo on forehead","piercing","freckles","beard","mustache","clean-shaven","birthmark","sharp jawline","soft features"]};function Wa(){const s=R.getInstance();for(const[e,t]of Object.entries(uh))s.initializeDefaults(e,[...t])}function _n(){return R.getInstance().getRegisteredCategories().some(t=>String(t).startsWith("appearance."))}function za(){_n()||Wa()}const dh=Object.values(Ia),hh=Object.entries(Rt).map(([s,e])=>({class:s,cantrips:e.cantrips,spells_by_level:e.spells_by_level}));function qa(){const s=R.getInstance();s.initializeDefaults("spells",dh);for(const e of hh){const t=`spells.${e.class}`;s.initializeDefaults(t,[e])}}function An(){return R.getInstance().getRegisteredCategories().some(t=>String(t).startsWith("spells"))}function Ot(){An()||qa()}const ph=Object.values(Ze).map(s=>({...s,source:"default",spawnWeight:s.spawnWeight??1,tags:s.tags??[]}));function Ua(){R.getInstance().initializeDefaults("equipment",ph)}function kn(){return R.getInstance().getRegisteredCategories().some(t=>t==="equipment")}function Ga(){kn()||Ua()}function ja(){R.getInstance().initializeDefaults("races",[...Ps])}function Tn(){return R.getInstance().getRegisteredCategories().some(t=>t==="races")}function Va(){Tn()||ja()}function Ya(){R.getInstance().initializeDefaults("races.data",[...$s])}function Cn(){return R.getInstance().getRegisteredCategories().some(t=>t==="races.data")}function In(){Cn()||Ya()}function Za(){R.getInstance().initializeDefaults("classes",[...Ds])}function En(){return R.getInstance().getRegisteredCategories().some(t=>t==="classes")}function Xa(){En()||Za()}function mh(){Wa(),qa(),Ua(),ja(),Ya(),Za(),Ka(),Qa()}function gh(){za(),Ot(),Ga(),Va(),In(),Xa(),Ja(),Rn()}function Ka(){const s=R.getInstance(),e={};for(const a of ga)e[a.class]||(e[a.class]=[]),e[a.class].push(a);s.initializeDefaults("classFeatures",[...ga]);for(const[a,i]of Object.entries(e)){const n=`classFeatures.${a}`;s.initializeDefaults(n,i)}const t={};for(const a of fa)t[a.race]||(t[a.race]=[]),t[a.race].push(a);s.initializeDefaults("racialTraits",[...fa]);for(const[a,i]of Object.entries(t)){const n=`racialTraits.${a}`;s.initializeDefaults(n,i)}}function Mn(){return R.getInstance().getRegisteredCategories().some(t=>t==="classFeatures"||t==="racialTraits")}function Ja(){Mn()||Ka()}function Qa(){const s=R.getInstance(),e={STR:[],DEX:[],CON:[],INT:[],WIS:[],CHA:[]};for(const t of ya)e[t.ability].push(t);s.initializeDefaults("skills",[...ya]);for(const[t,a]of Object.entries(e)){const i=`skills.${t}`;s.initializeDefaults(i,a)}}function Dn(){const s=R.getInstance();return s.hasCustomData("skills")||s.getDefaults("skills").length>0}function Rn(){Dn()||Qa()}class Pn{static select(e){Va();const t=R.getInstance(),a=t.get("races"),i=t.getWeights("races"),n=t.getMode("races")||"default";return ye.select(a,i,e,n)}}const ci=class ci{static suggest(e,t){Xa();const a=R.getInstance(),i=a.get("classes"),n=this.calculateAllAffinities(e,i),r=this.calculateProbabilities(n),o=a.getWeights("classes"),l=this.applyCustomWeights(r,o,i),c=Object.entries(l).map(([u,d])=>[u,d]);return t.weightedChoice(c)}static calculateAllAffinities(e,t){const a={};for(const i of t)a[i]=this.calculateClassAffinity(e,i);return a}static calculateClassAffinity(e,t){let a=this.getCustomClassAudioPreferences(t);if(a||(a=Rs[t]),!a)return .5;let i=0;return i+=this.getTraitContribution(e,a.primary,a,1),a.secondary&&(i+=this.getTraitContribution(e,a.secondary,a,.5)),a.tertiary&&(i+=this.getTraitContribution(e,a.tertiary,a,.25)),i}static getCustomClassAudioPreferences(e){try{const a=R.getInstance().get("classes.data");if(a&&Array.isArray(a)){const i=a.find(n=>n.name===e);if(i&&i.audio_preferences)return i.audio_preferences}}catch{}}static getTraitContribution(e,t,a,i){switch(t){case"bass":return e.bass_dominance*(a.bass||1)*i;case"treble":return e.treble_dominance*(a.treble||1)*i;case"mid":return e.mid_dominance*(a.mid||1)*i;case"amplitude":return e.average_amplitude*(a.amplitude||1)*i;case"chaos":{const n=e.bass_dominance,r=e.treble_dominance,o=e.mid_dominance;return(1-(Math.max(n,r,o)-Math.min(n,r,o)))*i}default:return 0}}static calculateProbabilities(e){const t=Object.keys(e),a=t.length;if(a===0)return{};const i=Object.values(e).reduce((d,h)=>d+h,0);if(i===0){const d=1/a,h={};for(const p of t)h[p]=d;return h}const n=this.BASELINE_PROBABILITY*a,r=1-n,o=n>=1?1/a:this.BASELINE_PROBABILITY,l=n>=1?0:r,c={};for(const d of t){const h=e[d]/i;c[d]=o+h*l}const u=Object.values(c).reduce((d,h)=>d+h,0);for(const d of t)c[d]/=u;return c}static applyCustomWeights(e,t,a){if(Object.keys(t).length===0)return e;const i=Object.values(t).reduce((l,c)=>l+Math.max(0,c),0);if(i===0)return e;const n={};for(const[l,c]of Object.entries(t))a.includes(l)&&(n[l]=Math.max(0,c)/i);const r=1-Object.values(n).reduce((l,c)=>l+c,0),o=Object.entries(e).filter(([l])=>!t[l]).reduce((l,[,c])=>l+c,0);for(const l of a)!t[l]&&e[l]&&(o>0?n[l]=e[l]/o*r:n[l]=r/a.length);return n}};ci.BASELINE_PROBABILITY=.04;let Bt=ci;class Et{static getSpicedValue(e,t,a){let i;switch(e){case"bass":i=a.rms_energy??a.dynamic_range??t;break;case"mid":a.spectral_centroid!==void 0?i=Math.min(1,Math.max(0,(a.spectral_centroid-100)/9900)):i=a.dynamic_range??t;break;case"treble":a.zero_crossing_rate!==void 0?i=a.zero_crossing_rate:a.spectral_centroid!==void 0?i=Math.min(1,Math.max(0,(a.spectral_centroid-100)/9900)):i=t;break}return(t+i)/2}static createFrequencyMappings(e){const t=["STR","DEX","CON","INT","WIS","CHA"],a=new Map,i=e.shuffle(t),n=["bass","mid","treble"];for(let r=0;r<3;r++){const o=n[r],l=i[r*2],c=i[r*2+1],u=e.random()<.5;a.set(o,{abilities:[l,c],firstHasSpice:u})}return a}static calculateBaseScores(e,t){const{bass_dominance:a,mid_dominance:i,treble_dominance:n}=e,r=this.createFrequencyMappings(t),o={STR:8,DEX:8,CON:8,INT:8,WIS:8,CHA:8},l=3.5,c=3.5;for(const[u,d]of r){const[h,p]=d.abilities,g=d.firstHasSpice;let m;switch(u){case"bass":m=a;break;case"mid":m=i;break;case"treble":m=n;break}const f=g?this.getSpicedValue(u,m,e):m,v=g?m:this.getSpicedValue(u,m,e),w=t.random(),E=t.random();o[h]=Math.floor(8+w*c+f*l),o[p]=Math.floor(8+E*c+v*l)}return o}static applyRacialBonuses(e,t){const a=zt(t);if(!a)return console.warn(`Unknown race: "${t}", using no ability bonuses`),{...e};const i=a.ability_bonuses,n={...e};if(i)for(const[r,o]of Object.entries(i)){const l=r;n[l]=Math.min(20,(n[l]||0)+(o||0))}return n}static calculateModifiers(e){return{STR:Math.floor((e.STR-10)/2),DEX:Math.floor((e.DEX-10)/2),CON:Math.floor((e.CON-10)/2),INT:Math.floor((e.INT-10)/2),WIS:Math.floor((e.WIS-10)/2),CHA:Math.floor((e.CHA-10)/2)}}}class xn{static getSkillListDefinition(e){try{const a=R.getInstance().get("skillLists");return Array.isArray(a)?a.find(i=>i.class===e):void 0}catch{return}}static assignSkills(e,t,a){const i=pe.getInstance(),n=i.getAllSkills(),r={};for(const h of n)r[h.id]="none";const o=Ea(e);if(!o)return console.warn(`SkillAssigner: Unknown class "${e}", using default skill assignment`),r;const l=this.validateSkills(o.available_skills,i),c=a?this.filterSkillsByPrerequisites(l,i,a):l,u=this.getSkillListDefinition(e),d=this.selectSkills(c,o.skill_count,t,u?.selectionWeights);for(const h of d)r[h]="proficient";if(o.has_expertise&&o.expertise_count){const h=this.selectSkills(d,o.expertise_count,t);for(const p of h)r[p]="expertise"}return r}static validateSkills(e,t){const a=[];for(const i of e)t.isValidSkill(i)?a.push(i):console.warn(`SkillAssigner: Invalid skill ID "${i}" not found in SkillQuery. Skipping.`);return a}static filterSkillsByPrerequisites(e,t,a){const i=[];for(const n of e){const r=t.getSkill(n);r&&(r.prerequisites&&!we.validateSkillPrerequisites(r.prerequisites,a).valid||i.push(n))}return i}static selectSkills(e,t,a,i){if(!i||i.mode==="default")return this.selectSkillsEqualWeight(e,t,a);const{weights:n,mode:r}=i,o=[];for(const u of e){const d=n[u];if(d!==0)if(r==="absolute"){if(d!==void 0){const h=Math.round(d*10);for(let p=0;p<h;p++)o.push(u)}}else{const p=Math.round((d??1)*10);for(let g=0;g<p;g++)o.push(u)}}if(o.length===0)return this.selectSkillsEqualWeight(e,t,a);for(let u=o.length-1;u>0;u--){const d=Math.floor(a.random()*(u+1));[o[u],o[d]]=[o[d],o[u]]}const l=[],c=new Set;for(const u of o)if(!c.has(u)&&(l.push(u),c.add(u),l.length>=t))break;return l}static selectSkillsEqualWeight(e,t,a){const i=[...e];for(let n=i.length-1;n>0;n--){const r=Math.floor(a.random()*(n+1));[i[n],i[r]]=[i[r],i[n]]}return i.slice(0,t)}}const fh=["Wizard","Sorcerer","Warlock","Bard","Cleric","Druid","Paladin"];class Nn{static generate(e,t,a){za();const i=new Se(e),n=R.getInstance(),r=n.get("appearance.bodyTypes"),o=n.getWeights("appearance.bodyTypes"),l=n.getMode("appearance.bodyTypes")||"default",c=n.get("appearance.skinTones"),u=n.getWeights("appearance.skinTones"),d=n.getMode("appearance.skinTones")||"default",h=n.get("appearance.hairStyles"),p=n.getWeights("appearance.hairStyles"),g=n.getMode("appearance.hairStyles")||"default",m=n.get("appearance.hairColors"),f=n.getWeights("appearance.hairColors"),v=n.getMode("appearance.hairColors")||"default",w=n.get("appearance.eyeColors"),E=n.getWeights("appearance.eyeColors"),I=n.getMode("appearance.eyeColors")||"default",D=n.get("appearance.facialFeatures"),x=n.getWeights("appearance.facialFeatures"),N=n.getMode("appearance.facialFeatures")||"default",$=ye.select(r,o,i,l),F=ye.select(c,u,i,d),te=ye.select(h,p,i,g),Q=ye.select(m,f,i,v),X=ye.select(w,E,i,I),de=i.randomInt(1,4),ee=ye.selectMultiple(D,x,i,de,N),H=a.color_palette,P=H?.primary_color,L=H?.secondary_color,_e=H?.accent_color;let ge;return fh.includes(t)&&(ge=this.generateAuraColor(t,H?.primary_color)),{body_type:$,skin_tone:F,hair_style:te,hair_color:Q,eye_color:X,facial_features:ee,primary_color:P,secondary_color:L,accent_color:_e,aura_color:ge}}static generateAuraColor(e,t){return t?this.adjustColorBrightness(t,1.3):{Wizard:"#4169E1",Sorcerer:"#DC143C",Warlock:"#8B008B",Bard:"#FFD700",Cleric:"#F0E68C",Druid:"#228B22",Paladin:"#FFE4B5"}[e]||"#FFFFFF"}static adjustColorBrightness(e,t){const a=e.replace("#",""),i=parseInt(a.substring(0,2),16),n=parseInt(a.substring(2,4),16),r=parseInt(a.substring(4,6),16),o=Math.min(255,Math.floor(i*t)),l=Math.min(255,Math.floor(n*t)),c=Math.min(255,Math.floor(r*t)),u=d=>d.toString(16).padStart(2,"0");return`#${u(o)}${u(l)}${u(c)}`}}class va{static isSpellcaster(e){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(e)}static getSpellSlots(e,t){if(!this.isSpellcaster(e))return{};const a={};for(let n=0;n<=9;n++)a[n]={total:0,used:0};const i=Da(e,t);if(!i)return a;for(const[n,r]of Object.entries(i)){const o=parseInt(n,10);a[o]={total:r,used:0}}return a}static getCantrips(e){if(!this.isSpellcaster(e))return[];Ot();const t=R.getInstance(),a=la(e),i=a?[...a.cantrips]:[],n=`spells.${e}`,r=t.get(n);for(const o of r){const l=o;if(l.cantrips&&l.cantrips.length>0)for(const c of l.cantrips)i.includes(c)||i.push(c)}return i}static getKnownSpells(e,t,a){if(!this.isSpellcaster(e))return[];Ot();const i=R.getInstance(),n=la(e);if(!n||!n.spells_by_level)return[];[...n.cantrips];const r={};for(const[d,h]of Object.entries(n.spells_by_level))r[Number(d)]=[...h];const o=`spells.${e}`,l=i.get(o);for(const d of l){const h=d;if(h.cantrips&&h.cantrips.length>0)for(const p of h.cantrips);if(h.spells_by_level)for(const[p,g]of Object.entries(h.spells_by_level)){const m=Number(p);r[m]||(r[m]=[]);for(const f of g)r[m].includes(f)||r[m].push(f)}}const c={spells_by_level:r},u=[];for(let d=1;d<=t&&d<=9;d++){const h=c.spells_by_level[d];h&&h.length>0&&u.push(...h)}return a?this.filterSpellsByPrerequisites(u,a):u}static filterSpellsByPrerequisites(e,t){const a=[];for(const i of e){const n=Ia[i];if(!n){a.push(i);continue}n.prerequisites&&!De.validateSpellPrerequisites(n.prerequisites,t).valid||a.push(i)}return a}static initializeSpells(e,t,a){return{spell_slots:this.getSpellSlots(e,t),known_spells:this.getKnownSpells(e,t,a),cantrips:this.getCantrips(e)}}static getSpellCountAtLevel(e,t){return t[e]?.total||0}static useSpellSlot(e,t){if(t<1||t>9)throw new Error(`Invalid spell level: ${t}. Must be between 1 and 9.`);const a={...e},i=a[t];return i&&i.used<i.total&&(a[t]={...i,used:i.used+1}),a}static restoreSpellSlots(e,t){const a={...e};if(t!==void 0){const i=a[t];i&&(a[t]={...i,used:0})}else for(let i=1;i<=9;i++){const n=a[i];n&&(a[i]={...n,used:0})}return a}static filterCharacterSpells(e){if(!e.spells)return e;const t=this.filterSpellsByPrerequisites(e.spells.known_spells||[],e),a=this.filterSpellsByPrerequisites(e.spells.cantrips||[],e);return{...e,spells:{...e.spells,known_spells:t,cantrips:a}}}}const ba={Barbarian:{weapons:["Greataxe","Handaxe"],armor:["No Armor"],items:["Explorer's Pack","Javelin"]},Bard:{weapons:["Rapier","Dagger","Dagger"],armor:["Leather Armor"],items:["Lute","Entertainer's Pack"]},Cleric:{weapons:["Mace","Light Crossbow"],armor:["Scale Mail","Shield"],items:["Holy Symbol","Priest's Pack","Healer's Kit"]},Druid:{weapons:["Quarterstaff","Dagger"],armor:["Leather Armor","Shield"],items:["Druidic Focus","Explorer's Pack"]},Fighter:{weapons:["Longsword"],armor:["Chain Mail","Shield"],items:["Martial Melee Weapon","Bedroll","Rope"]},Monk:{weapons:["Shortsword","Martial Arts"],armor:["No Armor"],items:["Insignia","Traveler's Pack","Dart"]},Paladin:{weapons:["Longsword"],armor:["Chain Mail","Shield"],items:["Holy Symbol","Priest's Pack"]},Ranger:{weapons:["Longsword","Shortsword","Longbow","Dagger"],armor:["Leather Armor"],items:["Explorer's Pack"]},Rogue:{weapons:["Rapier","Hand Crossbow","Dagger"],armor:["Leather Armor"],items:["Burglar's Pack","Thieves' Tools"]},Sorcerer:{weapons:["Light Crossbow","Dagger"],armor:["Leather Armor"],items:["Arcane Focus","Dungeoneer's Pack"]},Warlock:{weapons:["Light Crossbow","Dagger"],armor:["Leather Armor"],items:["Arcane Focus","Scholar's Pack"]},Wizard:{weapons:["Quarterstaff","Dagger"],armor:["No Armor"],items:["Spellbook","Component Pouch","Scholar's Pack","Ink & Quill"]}};function $n(s){if(s in ba)return ba[s];try{const e=R.getInstance(),t=`classStartingEquipment.${s}`,a=e.get(t);if(a&&Array.isArray(a)&&a.length>0){const i=a[0];return{weapons:i.weapons,armor:i.armor,items:i.items}}}catch{}}function wa(s,e,t){s.ability_scores[e]+=t;const a=s.ability_scores[e];s.ability_modifiers[e]=Math.floor((a-10)/2)}function Sa(s,e,t){const a=s.skills[e];if(!a||a==="none"){s.skills[e]=t;return}t==="expertise"&&(s.skills[e]=t)}class le{static equipItem(e,t,a){const i={applied:!1,count:0,errors:[]};if(e.equipment_effects||(e.equipment_effects=[]),this.findEquipmentEffect(e,t.name,a))return i.applied=!0,i;if(t.properties){const o=this.validateRequirements(e,t,t.properties);if(o.length>0)return i.errors.push(...o),i}const r={source:t.name,instanceId:a,effects:t.properties?[...t.properties]:[],features:[],skills:[],spells:[]};if(t.properties)for(const o of t.properties)try{this.applyProperty(e,o,t.name),i.count++}catch(l){const c=l instanceof Error?l.message:String(l);i.errors.push(`Failed to apply property from ${t.name}: ${c}`)}return t.grantsFeatures&&(this.applyEquipmentFeatures(e,t,a,r.features),i.count+=r.features.length),t.grantsSkills&&(this.applyEquipmentSkills(e,t,a,r.skills),i.count+=r.skills.length),t.grantsSpells&&(r.spells=t.grantsSpells.map(o=>({spellId:o.spellId,level:o.level,uses:o.uses,recharge:o.recharge})),this.addSpellsToCharacter(e,t.grantsSpells),i.count+=r.spells.length),e.equipment_effects.push(r),i.applied=i.errors.length===0,i}static unequipItem(e,t,a){const i={applied:!1,count:0,errors:[]};if(!e.equipment_effects)return i.errors.push("No equipment effects found on character"),i;const n=e.equipment_effects.findIndex(o=>o.source===t&&o.instanceId===a);if(n===-1)return i.errors.push(`Equipment "${t}" effects not found`),i;const r=e.equipment_effects[n];return r.effects.length>0&&(this.removeProperties(e,r.effects),i.count+=r.effects.length),r.features.length>0&&(this.removeEquipmentFeatures(e,t,a),i.count+=r.features.length),r.skills.length>0&&(this.removeEquipmentSkills(e,r.skills),i.count+=r.skills.length),r.spells&&r.spells.length>0&&(this.removeSpellsFromCharacter(e,r.spells),i.count+=r.spells.length),e.equipment_effects.splice(n,1),i.applied=i.count>0,i}static reapplyEquipmentEffects(e){const t={applied:!1,count:0,errors:[]};if(!e.equipment_effects||e.equipment_effects.length===0)return t;const a=[...e.equipment_effects];e.equipment_effects=[];for(const i of a){const n={source:i.source,instanceId:i.instanceId,effects:[...i.effects],features:[...i.features],skills:[...i.skills],spells:i.spells?[...i.spells]:[]};for(const r of i.effects)try{this.applyProperty(e,r,i.source),t.count++}catch(o){const l=o instanceof Error?o.message:String(o);t.errors.push(`Failed to reapply property from ${i.source}: ${l}`)}for(const r of i.features)this.addFeatureToCharacter(e,r),t.count++;for(const r of i.skills)this.addSkillToCharacter(e,r),t.count++;i.spells&&(this.addSpellsToCharacter(e,i.spells),t.count+=i.spells.length),e.equipment_effects.push(n)}return t.applied=t.count>0,t}static getActiveEffects(e){if(!e.equipment_effects)return[];const t=[];for(const a of e.equipment_effects)t.push(...a.effects);return t}static evaluateACFormula(e,t){const a=e.trim();if(/^\d+$/.test(a))return parseInt(a,10);const i=a.match(/^(\d+)\s*\+\s*min\(DEX,\s*(\d+)\)$/i);if(i){const r=parseInt(i[1],10),o=parseInt(i[2],10);return r+Math.min(t,o)}const n=a.match(/^(\d+)\s*\+\s*DEX$/i);if(n)return parseInt(n[1],10)+t;throw new Error(`Unknown AC formula: "${e}"`)}static applyProperty(e,t,a){if(!(t.condition&&!this.checkCondition(t.condition,e)))switch(t.type){case"stat_bonus":case"skill_proficiency":case"ability_unlock":case"passive_modifier":this.applyPropertyViaFeatureEffectApplier(e,t);break;case"special_property":this.trackSpecialProperty(e,t,a);break;case"damage_bonus":this.trackDamageBonus(e,t,a);break;case"stat_requirement":this.trackStatRequirement(e,t,a);break;default:throw new Error(`Unknown property type: ${t.type}`)}}static applyPropertyViaFeatureEffectApplier(e,t){switch(t.type){case"stat_bonus":this.applyStatBonus(e,t);break;case"skill_proficiency":this.applySkillProficiency(e,t);break;case"ability_unlock":this.applyAbilityUnlock(e,t);break;case"passive_modifier":this.applyPassiveModifier(e,t);break}}static applyStatBonus(e,t){const a=t.target,i=t.value;if(Pe(a)){wa(e,a,i);return}}static applySkillProficiency(e,t){const a=t.target.toLowerCase(),i=t.value;Sa(e,a,i)}static applyAbilityUnlock(e,t){}static applyPassiveModifier(e,t){const a=t.target,i=t.value;if(a==="speed"){e.speed+=i;return}if(a==="ac"||a==="armor_class"){if(typeof i=="string"){const n=e.ability_modifiers.DEX??0;e.armor_class=le.evaluateACFormula(i,n)}else e.armor_class+=i;return}if(a==="max_hp"||a==="hp_max"){e.hp.max+=i,e.hp.current+=i;return}}static trackSpecialProperty(e,t,a){}static trackDamageBonus(e,t,a){}static removeProperties(e,t){for(let a=t.length-1;a>=0;a--){const i=t[a];switch(i.type){case"stat_bonus":this.removeStatBonus(e,i);break;case"skill_proficiency":this.removeSkillProficiency(e,i);break;case"passive_modifier":this.removePassiveModifier(e,i);break}}}static removeStatBonus(e,t){const a=t.target,i=t.value;Pe(a)&&wa(e,a,-i)}static removeSkillProficiency(e,t){const a=t.target.toLowerCase();e.skills[a]="none"}static removePassiveModifier(e,t){const a=t.target,i=t.value;a==="speed"?e.speed-=i:a==="ac"||a==="armor_class"?typeof i=="string"?e.armor_class=10+(e.ability_modifiers.DEX??0):e.armor_class-=i:(a==="max_hp"||a==="hp_max")&&(e.hp.max-=i,e.hp.current=Math.min(e.hp.current,e.hp.max))}static applyEquipmentFeatures(e,t,a,i){if(t.grantsFeatures)for(const n of t.grantsFeatures)if(typeof n=="string"){const r=re.getInstance(),o=r.getClassFeatureById(n),l=o?void 0:r.getRacialTraitById(n);if(o||l){const u={featureId:n,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};i.push(u),this.addFeatureToCharacter(e,u)}}else{const r={featureId:n.id,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};if(i.push(r),this.addFeatureToCharacter(e,r),n.effects)for(const o of n.effects)this.applyProperty(e,o,t.name)}}static applyEquipmentSkills(e,t,a,i){if(t.grantsSkills)for(const n of t.grantsSkills){const r={skillId:n.skillId,level:n.level,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};i.push(r),this.addSkillToCharacter(e,r)}}static addSpellsToCharacter(e,t){e.spells||(e.spells={spell_slots:{},known_spells:[],cantrips:[]});for(const a of t)a.level===0?e.spells.cantrips.includes(a.spellId)||e.spells.cantrips.push(a.spellId):e.spells.known_spells.includes(a.spellId)||e.spells.known_spells.push(a.spellId)}static removeSpellsFromCharacter(e,t){if(e.spells)for(const a of t)if(a.level===0){const i=e.spells.cantrips.indexOf(a.spellId);i>-1&&e.spells.cantrips.splice(i,1)}else{const i=e.spells.known_spells.indexOf(a.spellId);i>-1&&e.spells.known_spells.splice(i,1)}}static addFeatureToCharacter(e,t){e.class_features.includes(t.featureId)||e.class_features.push(t.featureId)}static addSkillToCharacter(e,t){const a=t.skillId.toLowerCase();Sa(e,a,t.level)}static removeEquipmentFeatures(e,t,a){if(!e.equipment_effects)return;const i=e.equipment_effects.filter(n=>n.source===t&&n.instanceId===a).flatMap(n=>n.features);for(const n of i){const r=e.class_features.indexOf(n.featureId);r>-1&&e.class_features.splice(r,1)}}static removeEquipmentSkills(e,t){for(const a of t){const i=a.skillId.toLowerCase();e.skills[i]="none"}}static checkCondition(e,t){switch(e.type){case"while_equipped":return!0;case"on_hit":case"on_damage_taken":return!1;case"wielder_race":return t.race===e.value;case"wielder_class":return t.class===e.value;case"at_time_of_day":return!0;case"vs_creature_type":return!1;case"custom":return!0;default:return!0}}static findEquipmentEffect(e,t,a){if(e.equipment_effects)return e.equipment_effects.find(i=>i.source===t&&i.instanceId===a)}static validateRequirements(e,t,a){const i=[];for(const n of a)if(n.type==="stat_requirement"){const r=n.target,o=n.value;if(Pe(r)){const l=e.ability_scores[r];l<o&&i.push(`${t.name} requires ${r} ${o}, but character has ${r} ${l}`)}}return i}static trackStatRequirement(e,t,a){}}class dt{static ensureInitialized(){Ga()}static getEquipmentDataStatic(e){return dt.getEquipmentData(e)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static getStartingEquipment(e){this.ensureInitialized();const t=$n(e);return t?{weapons:[...t.weapons],armor:[...t.armor],items:[...t.items]}:{weapons:[],armor:[],items:[]}}static initializeEquipment(e){this.ensureInitialized();const t=this.getStartingEquipment(e),a=[],i=[],n=[],r=[...t.weapons,...t.armor,...t.items];for(const l of r){const c=this.getEquipmentData(l);if(c){let u;c.type==="weapon"?u=a:c.type==="armor"?u=i:u=n;const d=u.find(h=>h.name===l);d?d.quantity+=1:u.push({name:l,quantity:1,equipped:!1})}else{console.warn(`EquipmentGenerator: Unknown item "${l}" in starting equipment.`);const u=n.find(d=>d.name===l);u?u.quantity+=1:n.push({name:l,quantity:1,equipped:!1})}}const o=this.getAmmunitionType(e,t.weapons);if(o){const l=this.getAmmunitionQuantity(e),c=n.find(u=>u.name===o);c?c.quantity+=l:n.push({name:o,quantity:l,equipped:!1})}return a.length>0&&(a[0].equipped=!0),i.length>0&&(i[0].equipped=!0),{weapons:a,armor:i,items:n,totalWeight:this.calculateTotalWeight(a,i,n),equippedWeight:this.calculateEquippedWeight(a,i,n)}}static addItem(e,t,a=1){this.ensureInitialized();const i=this.getEquipmentData(t);if(!i)return e;const n=this.cloneEquipment(e);let r;i.type==="weapon"?r=n.weapons:i.type==="armor"?r=n.armor:r=n.items;const o=r.find(l=>l.name===t);return o?a>0&&(o.quantity+=a):r.push({name:t,quantity:Math.max(0,a),equipped:!1}),n.totalWeight=this.calculateTotalWeight(n.weapons,n.armor,n.items),n.equippedWeight=this.calculateEquippedWeight(n.weapons,n.armor,n.items),n}static removeItem(e,t,a=1){if(this.ensureInitialized(),a<=0)return e;const i=this.cloneEquipment(e);let n;const r=this.getEquipmentData(t);if(!r)return e;r.type==="weapon"?n=i.weapons:r.type==="armor"?n=i.armor:n=i.items;const o=n.findIndex(l=>l.name===t);return o!==-1&&(n[o].quantity>a?n[o].quantity-=a:n.splice(o,1)),i.totalWeight=this.calculateTotalWeight(i.weapons,i.armor,i.items),i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static equipItem(e,t,a){this.ensureInitialized();const i=this.cloneEquipment(e),n=this.getEquipmentData(t);if(!n)return e;let r;n.type==="weapon"?r=i.weapons:n.type==="armor"?r=i.armor:r=i.items;const o=r.find(l=>l.name===t);if(o&&(o.equipped=!0,a)){const l=o.instanceId;le.equipItem(a,n,l)}return i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static unequipItem(e,t,a){this.ensureInitialized();const i=this.cloneEquipment(e),n=this.getEquipmentData(t);if(!n)return e;let r;n.type==="weapon"?r=i.weapons:n.type==="armor"?r=i.armor:r=i.items;const o=r.find(l=>l.name===t);if(o&&(o.equipped=!1,a)){const l=o.instanceId;le.unequipItem(a,t,l)}return i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static getInventoryList(e){return[...e.weapons,...e.armor,...e.items]}static getEquipmentByType(e,t){return e[t]}static calculateTotalWeight(e,t,a){let i=0;for(const n of e){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}for(const n of t){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}for(const n of a){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}return Math.round(i*100)/100}static calculateEquippedWeight(e,t,a){let i=0;for(const n of e)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}for(const n of t)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}for(const n of a)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}return Math.round(i*100)/100}static cloneEquipment(e){return{weapons:e.weapons.map(t=>({...t})),armor:e.armor.map(t=>({...t})),items:e.items.map(t=>({...t})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight}}static getAmmunitionType(e,t){const a=t.some(o=>o==="Longbow"),i=t.some(o=>o==="Shortbow"),n=t.some(o=>o==="Light Crossbow"),r=t.some(o=>o==="Hand Crossbow");return a||i?"Arrow":n||r?"Bolt":null}static getAmmunitionQuantity(e){return{Ranger:20,Fighter:20,Rogue:20}[e]||0}static addModification(e,t,a,i,n){this.ensureInitialized();const r=this.cloneEquipment(e),o=this.getEquipmentData(t);if(!o)return e;let l;o.type==="weapon"?l=r.weapons:o.type==="armor"?l=r.armor:l=r.items;const c=l.find(d=>d.name===t);if(!c)return e;c.modifications||(c.modifications=[]),!i&&!c.instanceId&&(c.instanceId=`${t}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const u=i||c.instanceId;return c.modifications.push(a),c.equipped&&n&&(le.unequipItem(n,t,u),this.applyCombinedEffects(n,o,a,u)),r}static removeModification(e,t,a,i){this.ensureInitialized();const n=this.cloneEquipment(e),r=this.getEquipmentData(t);if(!r)return e;let o;r.type==="weapon"?o=n.weapons:r.type==="armor"?o=n.armor:o=n.items;const l=o.find(h=>h.name===t);if(!l||!l.modifications)return e;const c=l.equipped,u=l.instanceId,d=l.modifications.findIndex(h=>h.id===a);if(d!==-1&&l.modifications.splice(d,1),c&&i&&(le.unequipItem(i,t,u),le.equipItem(i,r,u),l.modifications))for(const h of l.modifications)this.applyCombinedEffects(i,r,h,u);return n}static getActiveEffects(e,t,a){this.ensureInitialized();const i=this.getEquipmentData(t);if(!i)return[];let n;i.type==="weapon"?n=e.weapons:i.type==="armor"?n=e.armor:n=e.items;const r=n.find(l=>l.name===t&&(!a||l.instanceId===a));if(!r)return[];const o=i.properties?[...i.properties]:[];if(r.modifications)for(const l of r.modifications)l.properties&&o.push(...l.properties);return o}static applyCombinedEffects(e,t,a,i){const n={...t,properties:[...t.properties||[],...a.properties],grantsFeatures:a.addsFeatures,grantsSkills:a.addsSkills,grantsSpells:a.addsSpells};le.equipItem(e,n,i)}}class ht{static applyFeatureEffects(e,t){const a={applied:!1,count:0,errors:[]};if(!t.effects||t.effects.length===0)return a;for(const i of t.effects)try{this.applySingleEffect(e,i,t.id),a.applied=!0,a.count++}catch(n){const r=n instanceof Error?n.message:String(n);a.errors.push(`Failed to apply effect from ${t.id}: ${r}`)}return a}static applyMultipleEffects(e,t){const a={applied:!1,count:0,errors:[]};for(const i of t){const n=this.applyFeatureEffects(e,i);a.applied=a.applied||n.applied,a.count+=n.count,a.errors.push(...n.errors)}return a}static applySingleEffect(e,t,a){switch(t.type){case"stat_bonus":this.applyStatBonus(e,t);break;case"skill_proficiency":this.applySkillProficiency(e,t);break;case"ability_unlock":this.applyAbilityUnlock(e,t);break;case"passive_modifier":this.applyPassiveModifier(e,t);break;case"resource_grant":this.applyResourceGrant(e,t);break;case"spell_slot_bonus":this.applySpellSlotBonus(e,t);break;default:throw new Error(`Unknown effect type: ${t.type}`)}}static applyStatBonus(e,t){const a=t.target,i=t.value;if(Pe(a)){wa(e,a,i);return}e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"stat_bonus",target:t.target,value:t.value,condition:t.condition})}static applySkillProficiency(e,t){const a=t.target.toLowerCase(),i=t.value;Sa(e,a,i)}static applyAbilityUnlock(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"ability_unlock",target:t.target,value:t.value,condition:t.condition})}static applyPassiveModifier(e,t){const a=t.target,i=t.value;if(a==="speed"){e.speed+=i;return}if(a.endsWith("_max")){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"passive_modifier",target:t.target,value:t.value,condition:t.condition});return}e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"passive_modifier",target:t.target,value:t.value,condition:t.condition})}static applyResourceGrant(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"resource_grant",target:t.target,value:t.value,condition:t.condition})}static applySpellSlotBonus(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"spell_slot_bonus",target:t.target,value:t.value,condition:t.condition})}}class Vt{static registerExtensions(e){const t=R.getInstance();if(e.spells&&e.spells.length>0&&t.register("spells",e.spells),e.equipment&&e.equipment.length>0&&t.register("equipment",e.equipment),e.races&&e.races.length>0&&t.register("races",e.races),e.classes&&e.classes.length>0&&t.register("classes",e.classes),e.appearance){const{appearance:a}=e;a.bodyTypes&&a.bodyTypes.length>0&&t.register("appearance.bodyTypes",a.bodyTypes),a.skinTones&&a.skinTones.length>0&&t.register("appearance.skinTones",a.skinTones),a.hairColors&&a.hairColors.length>0&&t.register("appearance.hairColors",a.hairColors),a.hairStyles&&a.hairStyles.length>0&&t.register("appearance.hairStyles",a.hairStyles),a.eyeColors&&a.eyeColors.length>0&&t.register("appearance.eyeColors",a.eyeColors),a.facialFeatures&&a.facialFeatures.length>0&&t.register("appearance.facialFeatures",a.facialFeatures)}}static generate(e,t,a,i={}){const n=new Se(e),r=i.level||1,o=i.gameMode||"standard";Ja();const l=re.getInstance();i.extensions&&Vt.registerExtensions(i.extensions);let c;const u=i.subrace;let d;if(u==="pure")c=void 0,d=i.forceRace;else if(u!==void 0)if(i.forceRace)c=u,d=i.forceRace;else{const q=l.getRaceForSubrace(u);if(!q)throw new Error(`Cannot determine race for subrace "${u}". Either specify forceRace or ensure the subrace is registered in racial traits. Example: { forceRace: 'Elf', subrace: 'High Elf' }`);d=q,c=u}const h=d||Pn.select(n);if(c!==void 0&&u!=="pure"){const q=l.getAvailableSubraces(h);if(!q.includes(c))throw new Error(`Invalid subrace "${c}" for race "${h}". Available subraces: ${q.length>0?q.join(", "):"none"}`)}else if(c===void 0&&u===void 0){const ae=["pure",...l.getAvailableSubraces(h)],at=n.randomChoice(ae);c=at==="pure"?void 0:at}const p=i.forceClass||Bt.suggest(t,n),g=i.forceName||new Sn().generateName(e,a,t,p,i.deterministicName||!0),m=Et.calculateBaseScores(t,n),f=Et.applyRacialBonuses(m,h),v=Et.calculateModifiers(f),w=Ea(p);if(!w)throw new Error(`Unknown class: ${p}. Cannot generate character.`);const I=zt(h)?.speed??30,D=w.hit_die+v.CON,x=10+v.DEX,N=v.DEX,$=Ma[r],F=xn.assignSkills(p,n),te={STR:w.saving_throws.includes("STR"),DEX:w.saving_throws.includes("DEX"),CON:w.saving_throws.includes("CON"),INT:w.saving_throws.includes("INT"),WIS:w.saving_throws.includes("WIS"),CHA:w.saving_throws.includes("CHA")},Q=Nn.generate(e,p,t),X=l.getClassFeatures(p,r),de=c?l.getRacialTraitsForSubrace(h,c):l.getBaseRacialTraits(h),ee=dt.initializeEquipment(p),H={name:g,race:h,subrace:c,class:p,level:r,ability_scores:f,ability_modifiers:v,proficiency_bonus:$,hp:{current:D,max:D,temp:0},armor_class:x,initiative:N,speed:I,skills:F,saving_throws:te,racial_traits:[],class_features:[],appearance:Q,spells:{spell_slots:{},known_spells:[],cantrips:[]},equipment:ee,xp:{current:0,next_level:ct[r+1]||0},seed:e,generated_at:new Date().toISOString(),gameMode:o},P=va.initializeSpells(p,r,H),L=[];for(const q of X){const ae=l.validatePrerequisites(q,H);ae.valid||console.warn(`Feature "${q.name}" (${q.id}) failed prerequisite validation:`,ae.errors),L.push(q)}const _e=[];for(const q of de){const ae=l.validatePrerequisites(q,H);ae.valid||console.warn(`Trait "${q.name}" (${q.id}) failed prerequisite validation:`,ae.errors),_e.push(q)}const ge={name:g,race:h,subrace:c,class:p,level:r,ability_scores:f,ability_modifiers:v,proficiency_bonus:$,hp:{current:D,max:D,temp:0},armor_class:x,initiative:N,speed:I,skills:F,saving_throws:te,racial_traits:_e.map(q=>q.id),class_features:L.map(q=>q.id),appearance:Q,spells:P,equipment:ee,xp:{current:0,next_level:ct[r+1]||0},seed:e,generated_at:new Date().toISOString(),gameMode:o};if(ht.applyMultipleEffects(ge,_e),ht.applyMultipleEffects(ge,L),ee){const q=[...ee.weapons.filter(ae=>ae.equipped),...ee.armor.filter(ae=>ae.equipped),...ee.items.filter(ae=>ae.equipped)];for(const ae of q){const at=dt.getEquipmentDataStatic(ae.name);if(at){const er=ae.instanceId;le.equipItem(ge,at,er).applied||(ae.equipped=!1)}}}return va.filterCharacterSpells(ge)}}function yh(s){return typeof s=="string"&&["humanoid","beast","undead","dragon","fiend","construct","elemental","monstrosity"].includes(s)}function vh(s){return typeof s=="string"&&["common","uncommon","elite","boss"].includes(s)}function bh(s){return typeof s=="string"&&["brute","archer","support"].includes(s)}function wh(s){return typeof s=="string"&&["easy","medium","hard","deadly"].includes(s)}class Sh{static calculatePartyLevel(e){if(e.length===0)return 1;const t=e.map(a=>a.level||1);return bs(t)}static calculatePartyStrength(e){if(e.length===0)return 0;const t=e.reduce((c,u)=>c+(u.hp?.max||10),0),a=this.getAverageAC(e),i=this.getAverageDamage(e),n=e.length,r=a/10,o=t*r,l=i*n*10;return o+l}static getXPBudget(e,t){if(e.length===0)return 0;const a=e.map(i=>i.level||1);return Wt(a,t)}static getAverageAC(e){if(e.length===0)return 10;const t=e.reduce((a,i)=>a+(i.armor_class||10),0);return Math.round(t/e.length)}static getAverageHP(e){if(e.length===0)return 10;const t=e.reduce((a,i)=>a+(i.hp?.max||10),0);return Math.round(t/e.length)}static getPartySize(e){return e.length}static getAverageDamage(e){if(e.length===0)return 0;const t=e.reduce((a,i)=>a+this.estimateCharacterDamage(i),0);return Math.round(t/e.length)}static estimateCharacterDamage(e){const t=e.ability_scores,a=e.equipment?.weapons?.find(o=>o.equipped);let i,n;if(a?.damage?.dice){i=a.damage.dice;const o=a.weaponProperties?.includes("ranged")??!1,l=a.weaponProperties?.includes("finesse")??!1;n=Math.floor(((t?.[o||l?"DEX":"STR"]??10)-10)/2)}else i="1",n=Math.floor(((t?.STR??10)-10)/2);let r;try{const o=U.parseDiceFormula(i);r=o.diceCount*((o.diceSides+1)/2)+o.modifier+n}catch{r=n+1}return Math.round(r*10)/10}static analyzeParty(e){const t=this.calculatePartyLevel(e),a=this.getPartySize(e),i=this.getAverageAC(e),n=this.getAverageHP(e),r=this.getAverageDamage(e),o=this.calculatePartyStrength(e),l=this.getXPBudget(e,"easy"),c=this.getXPBudget(e,"medium"),u=this.getXPBudget(e,"hard"),d=this.getXPBudget(e,"deadly");return{averageLevel:t,partySize:a,averageAC:i,averageHP:n,averageDamage:r,totalStrength:o,easyXP:l,mediumXP:c,hardXP:u,deadlyXP:d}}}const ie=[];for(let s=0;s<256;++s)ie.push((s+256).toString(16).slice(1));function _h(s,e=0){return(ie[s[e+0]]+ie[s[e+1]]+ie[s[e+2]]+ie[s[e+3]]+"-"+ie[s[e+4]]+ie[s[e+5]]+"-"+ie[s[e+6]]+ie[s[e+7]]+"-"+ie[s[e+8]]+ie[s[e+9]]+"-"+ie[s[e+10]]+ie[s[e+11]]+ie[s[e+12]]+ie[s[e+13]]+ie[s[e+14]]+ie[s[e+15]]).toLowerCase()}let ea;const Ah=new Uint8Array(16);function kh(){if(!ea){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");ea=crypto.getRandomValues.bind(crypto)}return ea(Ah)}const Th=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ss={randomUUID:Th};function Ch(s,e,t){s=s||{};const a=s.random??s.rng?.()??kh();if(a.length<16)throw new Error("Random bytes length must be >= 16");return a[6]=a[6]&15|64,a[8]=a[8]&63|128,_h(a)}function Ih(s,e,t){return ss.randomUUID&&!s?ss.randomUUID():Ch(s)}class Eh{constructor(e={}){this.options={validateAudioUrls:!1,strict:!1,audioUrlValidationTimeout:5e3,resolveImageUrls:!1,...e}}async parse(e){const t=e.name,a=e.image,i=e.creator,n=e.description,r=e.genre,o=e.tags;let l=a;a&&this.options.resolveImageUrls&&(l=await z.arweaveGatewayManager.resolveUrl(a));const c=e.tracks||[],u=[];for(let d=0;d<c.length;d++)try{const h=await this.parseTrack(c[d],d);if(h){const p=await this.resolveTrackUrls(h);u.push(p)}}catch(h){if(this.options.strict)throw h;console.warn(`Failed to parse track at index ${d}:`,h)}return{name:t,description:n,image:l,creator:i,genre:r,tags:o,version:e.version,playlist_type:e.playlist_type,original_playlist_tx_id:e.original_playlist_tx_id,playlist_artist:e.playlist_artist,platform:e.platform,tracks:u}}async resolveTrackUrls(e){if(!this.options.resolveImageUrls)return e;const t={...e};return t.image_url&&(t.image_url=await z.arweaveGatewayManager.resolveUrl(t.image_url)),t.image_thumb_url&&(t.image_thumb_url=await z.arweaveGatewayManager.resolveUrl(t.image_thumb_url)),t}async parseTrack(e,t){const a=O.MetadataExtractor.parseMetadata(e.metadata);if(!a&&this.options.strict)throw new Error(`Failed to parse metadata for track at index ${t}`);const i=e.chain_name,n=e.token_address,r=e.token_id,o=e.tx_id,l=e.platform,c=e.id||(i==="AR"?`AR-${o}`:`${i}-${n}-${r}`),u=e.uuid||Ih(),d=O.MetadataExtractor.extractTitle(a||{}),h=O.MetadataExtractor.extractArtist(a||{}),g=typeof e.artwork_url=="string"&&e.artwork_url||typeof e.image_url=="string"&&e.image_url||null||O.MetadataExtractor.extractImageUrl(a||{}),m=O.MetadataExtractor.extractImageThumbUrl(a||{}),v=typeof e.audio_url=="string"&&e.audio_url||null||O.MetadataExtractor.extractAudioUrl(a||{}),w=O.MetadataExtractor.extractAudioUrlLossless(a||{}),E=O.MetadataExtractor.convertAttributes(a?.attributes),I=bn(a||{}),D=wn(e.selected_mix,E,I.mixes),x=D?.audio_url||v,N=D?D.audio_url_lossless:w;if(!x){if(this.options.strict)throw new Error(`No audio URL found for track: ${d||c}`);return console.warn(`Track ${c} has no audio URL - marked as Unsummonable`),null}if(this.options.validateAudioUrls&&!await this.validateAudioUrl(x)){if(this.options.strict)throw new Error(`Audio URL validation failed for track: ${d||c}`);return console.warn(`Track ${c} has invalid audio URL - marked as Unsummonable`),null}if(!d||!h||!g){if(this.options.strict)throw new Error(`Missing required fields for track: ${JSON.stringify({title:d,artist:h,imageUrl:g})}`);return null}const $=O.MetadataExtractor.extractDescription(a||{})??void 0,F=typeof a?.album=="string"?a.album:void 0,te=a?.duration?Number(a.duration):0,Q=O.MetadataExtractor.extractGenre(a||{}),X=O.MetadataExtractor.extractTags(a||{}),de=a?.bpm?Number(a.bpm):void 0,ee=typeof a?.key=="string"?a.key:void 0,H={id:c,uuid:u,playlist_index:t,chain_name:i,platform:l,title:d,artist:h,description:$,album:F,image_url:g,audio_url:x,audio_url_lossless:N&&N!==x?N:void 0,...D?{selected_mix:D.name}:{},duration:te,genre:Q,tags:X,bpm:de,key:ee,attributes:E||void 0,extras:I};return m&&(H.image_thumb_url=m),typeof a?.audio_ipfs_hash=="string"?H.audio_ipfs_hash=e.audio_ipfs_hash||a.audio_ipfs_hash:e.audio_ipfs_hash&&(H.audio_ipfs_hash=e.audio_ipfs_hash),typeof a?.artwork_ipfs_hash=="string"?H.artwork_ipfs_hash=e.artwork_ipfs_hash||a.artwork_ipfs_hash:e.artwork_ipfs_hash&&(H.artwork_ipfs_hash=e.artwork_ipfs_hash),i!=="AR"?(H.token_address=n,H.token_id=r):o&&(H.tx_id=o),H}async validateAudioUrl(e){const t=new AbortController,a=setTimeout(()=>t.abort(),this.options.audioUrlValidationTimeout);try{const i=await fetch(e,{method:"HEAD",signal:t.signal});return clearTimeout(a),i.ok}catch(i){return clearTimeout(a),i instanceof Error&&i.name==="AbortError"?console.warn(`Audio URL validation timed out after ${this.options.audioUrlValidationTimeout}ms: ${e}`):console.warn(`Audio URL validation failed for ${e}:`,i),!1}}}class Mh{constructor(e={}){this.canvas=null,this.context=null,typeof document<"u"&&(this.canvas=document.createElement("canvas"),this.canvas.width=100,this.canvas.height=100,this.context=this.canvas.getContext("2d",{willReadFrequently:!0}))}async extractPalette(e){try{if(!this.context||!this.canvas)throw new Error("Canvas not supported in this environment");const t=await z.arweaveGatewayManager.resolveUrl(e),a=await this.loadImage(t);this.context.drawImage(a,0,0,100,100);const i=this.context.getImageData(0,0,100,100),n=this.getPixels(i);if(n.length===0)return this.getFallbackPalette();let r=this.kMeans(n,4);r.length<4&&(r=this.medianCut(n,4));const o=this.sortColorsByFrequency(n,r);return this.analyzePalette(o)}catch(t){return console.warn("Color extraction failed, using fallback:",t),this.getFallbackPalette()}}loadImage(e){return new Promise((t,a)=>{const i=new Image;i.crossOrigin="Anonymous",i.onload=()=>t(i),i.onerror=n=>a(n),i.src=e})}getPixels(e){const t=[];for(let a=0;a<e.data.length;a+=4){const i=e.data[a],n=e.data[a+1],r=e.data[a+2];e.data[a+3]>=128&&t.push([i,n,r])}return t}kMeans(e,t){if(e.length===0)return[];if(e.length<t)return e;let a=[];const i=new Set;for(;a.length<t&&a.length<e.length;){const n=Math.floor(Math.random()*e.length);i.has(n)||(i.add(n),a.push([...e[n]]))}for(let n=0;n<10;n++){const r=Array.from({length:t},()=>[]);for(const l of e){let c=1/0,u=0;for(let d=0;d<a.length;d++){const h=this.distance(l,a[d]);h<c&&(c=h,u=d)}r[u].push(l)}const o=[];for(let l=0;l<t;l++)if(r[l].length===0)o.push(a[l]);else{const c=r[l].reduce((u,d)=>[u[0]+d[0],u[1]+d[1],u[2]+d[2]],[0,0,0]);o.push([Math.round(c[0]/r[l].length),Math.round(c[1]/r[l].length),Math.round(c[2]/r[l].length)])}if(o.every((l,c)=>l[0]===a[c][0]&&l[1]===a[c][1]&&l[2]===a[c][2]))break;a=o}return a}medianCut(e,t){if(e.length===0)return[];const a=[],i=[{pixels:[...e],depth:0}];for(;i.length<t&&i.length>0;){let n=i[0],r=0,o=0;for(let u=0;u<i.length;u++){const d=this.getBoxRange(i[u].pixels);d>o&&(o=d,n=i[u],r=u)}if(n.pixels.length<=1)break;const l=this.sortPixelsByChannel(n.pixels,n.depth%3),c=Math.floor(l.length/2);i.splice(r,1),i.push({pixels:l.slice(0,c),depth:n.depth+1},{pixels:l.slice(c),depth:n.depth+1})}for(const n of i)if(n.pixels.length>0){const r=n.pixels.reduce((o,l)=>[o[0]+l[0],o[1]+l[1],o[2]+l[2]],[0,0,0]);a.push([Math.round(r[0]/n.pixels.length),Math.round(r[1]/n.pixels.length),Math.round(r[2]/n.pixels.length)])}return a.slice(0,t)}getBoxRange(e){if(e.length===0)return 0;let t=1/0,a=-1/0,i=1/0,n=-1/0,r=1/0,o=-1/0;for(const[l,c,u]of e)t=Math.min(t,l),a=Math.max(a,l),i=Math.min(i,c),n=Math.max(n,c),r=Math.min(r,u),o=Math.max(o,u);return Math.max(a-t,n-i,o-r)}sortPixelsByChannel(e,t){return[...e].sort((a,i)=>a[t]-i[t])}distance(e,t){return Math.sqrt(Math.pow(e[0]-t[0],2)+Math.pow(e[1]-t[1],2)+Math.pow(e[2]-t[2],2))}sortColorsByFrequency(e,t){const a=new Map;for(const i of e){let n=t[0],r=1/0;for(const l of t){const c=this.distance(i,l);c<r&&(r=c,n=l)}const o=`${n[0]},${n[1]},${n[2]}`;a.set(o,(a.get(o)||0)+1)}return t.sort((i,n)=>{const r=`${i[0]},${i[1]},${i[2]}`,o=`${n[0]},${n[1]},${n[2]}`;return(a.get(o)||0)-(a.get(r)||0)})}analyzePalette(e){const t=e.map(c=>this.rgbToHex(c[0],c[1],c[2]));if(t.length===0)return this.getFallbackPalette();const a=t[0],i=t[1],n=t[2],r=this.getBrightness(e[0]),o=this.getSaturation(e[0]),l=o<.1;return{colors:t,primary_color:a,secondary_color:i,accent_color:n,brightness:r/255,saturation:o,is_monochrome:l}}getBrightness(e){return(e[0]*299+e[1]*587+e[2]*114)/1e3}getSaturation(e){const t=Math.max(e[0],e[1],e[2]),a=Math.min(e[0],e[1],e[2]);return t===0?0:(t-a)/t}rgbToHex(e,t,a){return"#"+[e,t,a].map(i=>{const n=i.toString(16);return n.length===1?"0"+n:n}).join("").toUpperCase()}getFallbackPalette(){return{colors:["#000000","#333333","#666666"],primary_color:"#000000",secondary_color:"#333333",accent_color:"#666666",brightness:0,saturation:0,is_monochrome:!0}}}const ta=z.Logger.for("BeatSubdivider"),Dh={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};class Qe{constructor(e){this.options={...Dh,...e},ta.debug("BeatSubdivider initialized",{options:this.options})}subdivide(e,t=y.DEFAULT_SUBDIVISION_CONFIG){if(ta.debug("Starting per-beat subdivision",{audioId:e.audioId,beatCount:e.beats.length,explicitAssignments:t.beatSubdivisions.size,defaultSubdivision:t.defaultSubdivision}),y.validateSubdivisionConfig(t),y.validateSubdivisionConfigAgainstBeats(t,e.beats.length),e.beats.length===0)return this.createEmptySubdividedBeatMap(e,t);const a=new Set;let i=0;const n=!!e.tempoSections&&e.tempoSections.length>1;let r=0;const o=m=>{let f=0;for(let v=m-1;v>=0&&(t.beatSubdivisions.get(v)??t.defaultSubdivision)==="triplet4";v--)f++;return f%2},l=[];for(let m=0;m<e.beats.length;m++){const f=t.beatSubdivisions.get(m)??t.defaultSubdivision;t.beatSubdivisions.has(m)&&r++,a.add(f);const v=y.getSubdivisionDensity(f);i=Math.max(i,v);const w=e.beats[m],E=m<e.beats.length-1?e.beats[m+1]:null,I=e.detectedBeatIndices.includes(m);if(f==="rest"||f==="half"&&w.beatInMeasure%2!==0)continue;const D=f==="dotted4"&&w.beatInMeasure%2!==0;if(f==="offbeat8"||f==="triplet4"&&o(m)===1||D||l.push({...w,isDetected:I,originalBeatIndex:m,subdivisionType:f}),E){const N=this.createInterpolatedBeatsForSubdivision(w,E,m,f,e,n,t,o(m));l.push(...N)}}const c=this.buildDetectedBeatIndices(l),u=e.beats.length,d=l.length,h=u>0?d/u:1,p={originalBeatCount:u,subdividedBeatCount:d,averageDensityMultiplier:h,explicitBeatCount:r,subdivisionsUsed:Array.from(a),hasMultipleTempos:n,maxDensity:i},g={audioId:e.audioId,duration:e.duration,beats:l,detectedBeatIndices:c,subdivisionConfig:t,downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections,subdivisionMetadata:p};return ta.debug("Per-beat subdivision complete",{originalBeats:u,subdividedBeats:d,densityMultiplier:h,subdivisionsUsed:Array.from(a)}),g}createInterpolatedBeatsForSubdivision(e,t,a,i,n,r,o,l=0){const c=[],u=n.quarterNoteInterval,d=r?this.getQuarterNoteIntervalForTimestamp(n,e.timestamp):u;switch(i){case"quarter":break;case"half":break;case"eighth":c.push(this.createInterpolatedBeat(e,t,.5,"eighth",d,this.options));break;case"sixteenth":for(let p=.25;p<1;p+=.25)c.push(this.createInterpolatedBeat(e,t,p,"sixteenth",d,this.options));break;case"triplet8":for(const p of[1/3,2/3])c.push(this.createInterpolatedBeat(e,t,p,"triplet8",d,this.options));break;case"triplet4":{l===0?c.push(this.createInterpolatedBeat(e,t,2/3,"triplet4",d,this.options)):(o.beatSubdivisions.get(a-1)??o.defaultSubdivision)==="triplet4"&&c.push(this.createInterpolatedBeat(e,t,1/3,"triplet4",d,this.options));break}case"dotted4":e.beatInMeasure%2!==0&&c.push(this.createInterpolatedBeat(e,t,.5,"dotted4",d,this.options));break;case"dotted8":c.push(this.createInterpolatedBeat(e,t,3/4,"dotted8",d,this.options));break;case"swing":c.push(this.createInterpolatedBeat(e,t,2/3,"swing",d,this.options));break;case"offbeat8":c.push(this.createInterpolatedBeat(e,t,.5,"offbeat8",d,this.options));break;case"rest":break;default:const h=i;throw new Error(`Unknown subdivision type: ${h}`)}return c}createEmptySubdividedBeatMap(e,t){return{audioId:e.audioId,duration:e.duration,beats:[],detectedBeatIndices:[],subdivisionConfig:t,downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections,subdivisionMetadata:{originalBeatCount:0,subdividedBeatCount:0,averageDensityMultiplier:1,explicitBeatCount:0,subdivisionsUsed:[t.defaultSubdivision],hasMultipleTempos:!!e.tempoSections&&e.tempoSections.length>1,maxDensity:1}}}createInterpolatedBeat(e,t,a,i,n,r){const o=t.timestamp-e.timestamp,l=e.timestamp+o*a,c=e.beatInMeasure+a,u=(e.intensity+t.intensity)/2,d=(e.confidence+t.confidence)/2;return{timestamp:l,beatInMeasure:c,isDownbeat:!1,measureNumber:e.measureNumber,intensity:u,confidence:d,isDetected:!1,subdivisionType:i}}findClosestBeat(e,t){let a=e[0],i=Math.abs(e[0].timestamp-t);for(const n of e){const r=Math.abs(n.timestamp-t);r<i&&(i=r,a=n)}return a}calculateBeatInMeasure(e,t,a){return(e-t)/a%4}calculateMeasureNumber(e,t,a,i){const r=(e-t)/a,o=i.segments[0]?.timeSignature?.beatsPerMeasure??4;return Math.floor(r/o)}buildDetectedBeatIndices(e){const t=[];for(let a=0;a<e.length;a++)e[a].isDetected&&t.push(a);return t}getTempoSectionForTimestamp(e,t){if(!(!e||e.length===0)){for(const a of e)if(t>=a.start&&t<a.end)return a;return e[e.length-1]}}getQuarterNoteIntervalForTimestamp(e,t){const a=this.getTempoSectionForTimestamp(e.tempoSections,t);return a?a.intervalSeconds:e.quarterNoteInterval}getTempoSectionForBeatIndex(e,t){if(!(!e||e.length===0)){for(const a of e)if(t>=a.startBeatIndex&&t<=a.endBeatIndex)return a;return e[e.length-1]}}static toJSON(e){const t=Array.from(e.subdivisionConfig.beatSubdivisions.entries()),a={audioId:e.audioId,duration:e.duration,beats:e.beats.map(i=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,isDetected:i.isDetected,originalBeatIndex:i.originalBeatIndex,subdivisionType:i.subdivisionType})),detectedBeatIndices:e.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:t,defaultSubdivision:e.subdivisionConfig.defaultSubdivision},downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections?.map(i=>({start:i.start,end:i.end,bpm:i.bpm,intervalSeconds:i.intervalSeconds,beatCount:i.beatCount,startBeatIndex:i.startBeatIndex,endBeatIndex:i.endBeatIndex})),subdivisionMetadata:{originalBeatCount:e.subdivisionMetadata.originalBeatCount,subdividedBeatCount:e.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:e.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:e.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:e.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:e.subdivisionMetadata.hasMultipleTempos,maxDensity:e.subdivisionMetadata.maxDensity}};return JSON.stringify(a)}static fromJSON(e){const t=JSON.parse(e),a=new Map(t.subdivisionConfig.beatSubdivisions||[]);return{audioId:t.audioId,duration:t.duration,beats:t.beats.map(i=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,isDetected:i.isDetected,originalBeatIndex:i.originalBeatIndex,subdivisionType:i.subdivisionType})),detectedBeatIndices:t.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:a,defaultSubdivision:t.subdivisionConfig.defaultSubdivision},downbeatConfig:t.downbeatConfig,tempoSections:t.tempoSections?.map(i=>({start:i.start,end:i.end,bpm:i.bpm,intervalSeconds:i.intervalSeconds,beatCount:i.beatCount,startBeatIndex:i.startBeatIndex,endBeatIndex:i.endBeatIndex})),subdivisionMetadata:{originalBeatCount:t.subdivisionMetadata.originalBeatCount,subdividedBeatCount:t.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:t.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:t.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:t.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:t.subdivisionMetadata.hasMultipleTempos,maxDensity:t.subdivisionMetadata.maxDensity}}}static async saveToFile(e,t){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:a}=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),i=Qe.toJSON(e);await a(t,i,"utf-8")}static async loadFromFile(e){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:t}=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),a=await t(e,"utf-8");return Qe.fromJSON(a)}}class Rh{constructor(e){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0,this.previousDirection="neutral",this.options={...y.DEFAULT_GROOVE_OPTIONS,...e}}setDifficulty(e){const t=y.getGroovePenaltiesForPreset(e.preset,e.customPenalties);this.options.hotnessLossOnMiss=t.hotnessLossOnMiss,this.options.hotnessLossOnBreak=t.hotnessLossOnBreak}recordHit(e,t,a=0,i="perfect"){if(this.hitCount++,this.lastBpm=t,i==="miss"||i==="wrongKey")return this.recordMiss(a);const n=this.pocketDirection;this.recentOffsets.push(e),this.recentOffsets.length>this.options.averagingWindowSize&&this.recentOffsets.shift(),this.updateRunningAverage(),this.pocketDirection=this.determineDirection(this.establishedOffset);const r=this.calculatePocketWindow(t),o=Math.abs(e-this.establishedOffset),l=this.calculateConsistency(o,r),c=this.hasPocket()&&o<=r;this.hasPocket()&&(c?(this.hotness=this.hotness+this.options.hotnessGainPerHit,this.streakLength++):this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnBreak),this.maxGrooveStreak=Math.max(this.maxGrooveStreak,this.streakLength));let u;const d=n!==this.pocketDirection,h=d||this.hotness===0;if(this.hotness>0&&!h)this.grooveStartTime===null&&(this.grooveStartTime=a??this.hitCount*(60/t)),this.maxHotness=Math.max(this.maxHotness,this.hotness),this.hotnessSamples.push(this.hotness),this.grooveHitCount++;else if(h&&this.grooveStartTime!==null&&this.grooveHitCount>0){const p=a??this.hitCount*(60/t),g=this.hotnessSamples.length>0?this.hotnessSamples.reduce((m,f)=>m+f,0)/this.hotnessSamples.length:0;u={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:g,duration:p-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:p},this.streakLength=0,this.hotness=0,this.resetGrooveStats()}return d&&(this.streakLength=0,this.hotness=0,this.resetGrooveStats()),this.previousDirection=this.pocketDirection,{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:l,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,inPocket:c,pocketWindow:r,endedGrooveStats:u}}recordMiss(e){const a=this.hotness>0&&this.grooveStartTime!==null&&this.grooveHitCount>0;this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnMiss),this.streakLength=0;const i=this.calculatePocketWindow(this.lastBpm);let n;if(a&&this.hotness===0){const r=e??this.hitCount*(60/this.lastBpm),o=this.hotnessSamples.length>0?this.hotnessSamples.reduce((l,c)=>l+c,0)/this.hotnessSamples.length:0;n={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:o,duration:r-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:r},this.resetGrooveStats()}return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:0,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,inPocket:!1,pocketWindow:i,endedGrooveStats:n}}getState(){const e=this.hotnessSamples.length>0?this.hotnessSamples.reduce((t,a)=>t+a,0)/this.hotnessSamples.length:0;return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,hitCount:this.hitCount,pocketWindow:this.calculatePocketWindow(this.lastBpm),grooveStartTime:this.grooveStartTime,grooveDuration:this.grooveStartTime!==null?this.grooveHitCount*(60/this.lastBpm):0,maxHotness:this.maxHotness,avgHotness:e,grooveHitCount:this.grooveHitCount}}reset(){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.resetGrooveStats()}getGrooveStats(e){if(this.grooveStartTime===null||this.grooveHitCount===0)return null;const t=e??this.hitCount*(60/this.lastBpm),a=this.hotnessSamples.length>0?this.hotnessSamples.reduce((i,n)=>i+n,0)/this.hotnessSamples.length:0;return{maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:a,duration:t-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:t}}resetGrooveStats(){this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0}hasPocket(){return this.hitCount>=this.options.minHitsForPocket}calculatePocketWindow(e){const a=y.getGrooveWindowMs(this.hotness)/1e3,i=120/e;return a*i}updateRunningAverage(){this.recentOffsets.length!==0&&(this.establishedOffset=this.recentOffsets.reduce((e,t)=>e+t,0)/this.recentOffsets.length)}calculateConsistency(e,t){if(!this.hasPocket())return 0;const a=e/t;return a>=1?0:1-a*a}determineDirection(e){return Math.abs(e)<this.options.neutralDeadZone?"neutral":e<0?"push":"pull"}}function Ph(s,e=y.DEFAULT_SUBDIVISION_CONFIG,t){const a=y.unifyBeatMap(s);return new Qe(t).subdivide(a,e)}function We(s){return"mergedBeats"in s?s.mergedBeats:s.beats}function ei(s,e){return"mergedBeats"in s?{...s,mergedBeats:e}:{...s,beats:e}}function xh(s,e,t){const a=We(s);if(e<0||e>=a.length)throw new Error(`beatIndex ${e} is out of bounds (beats array length: ${a.length})`);const i=a.map((n,r)=>{if(r!==e)return n;if(t===null){const{requiredKey:o,...l}=n;return l}return{...n,requiredKey:t}});return ei(s,i)}function Nh(s,e){const t=We(s),a=t.length;for(const r of e)if(r.beatIndex<0||r.beatIndex>=a)throw new Error(`beatIndex ${r.beatIndex} is out of bounds (beats array length: ${a})`);const i=new Map;for(const{beatIndex:r,key:o}of e)i.set(r,o);const n=t.map((r,o)=>{const l=i.get(o);if(l===void 0)return r;if(l===null){const{requiredKey:c,...u}=r;return u}return{...r,requiredKey:l}});return ei(s,n)}function $h(s){const e=We(s),t=new Map;for(let a=0;a<e.length;a++){const i=e[a];i.requiredKey!==void 0&&t.set(a,i.requiredKey)}return t}function Lh(s){const t=We(s).map(a=>{if(a.requiredKey===void 0)return a;const{requiredKey:i,...n}=a;return n});return ei(s,t)}function Oh(s){return We(s).some(t=>t.requiredKey!==void 0)}function Bh(s){return We(s).filter(t=>t.requiredKey!==void 0).length}function Fh(s){const e=We(s),t=new Set;for(const a of e)a.requiredKey!==void 0&&t.add(a.requiredKey);return Array.from(t).sort()}const J=z.Logger.for("SubdivisionPlaybackController");class Hh{constructor(e,t,a={}){this.unifiedMap=e,this.audioContext=t,this.options={...y.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS,...a};const i={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};this.subdivider=new Qe(i),this.state={isRunning:!1,isPaused:!1,currentTime:0,startTime:0,pauseTime:0,rafId:null,currentSubdivision:this.options.initialSubdivision,pendingSubdivision:null,pendingMeasureNumber:null,transitionTimestamp:null,scheduledBeats:new Map,thresholds:y.getAccuracyThresholdsForPreset("medium")},this.subscribers=new Set,this.regenerateBeats(),J.debug("SubdivisionPlaybackController initialized",{audioId:e.audioId,duration:e.duration,initialSubdivision:this.state.currentSubdivision,beatCount:e.beats.length})}get subdivision(){return this.state.currentSubdivision}get beatMap(){return this.unifiedMap}getOptions(){return{...this.options}}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}setSubdivision(e){if(!y.isValidSubdivisionType(e))throw new Error(`Invalid subdivision type: ${e}`);if(this.state.currentSubdivision===e)return;const t=this.state.currentSubdivision;switch(J.debug("Subdivision change requested",{from:t,to:e,transitionMode:this.options.transitionMode}),this.options.transitionMode){case"immediate":this.applySubdivisionChange(e,t);break;case"next-downbeat":this.state.pendingSubdivision=e,this.state.pendingMeasureNumber=null,J.debug("Subdivision change deferred until next downbeat",{pendingType:e,transitionMode:this.options.transitionMode});break;case"next-measure":{const i=this.getCurrentBeat()?.measureNumber??null;this.state.pendingSubdivision=e,this.state.pendingMeasureNumber=i,J.debug("Subdivision change deferred until next measure",{pendingType:e,transitionMode:this.options.transitionMode,currentMeasureNumber:i});break}default:this.applySubdivisionChange(e,t)}}setTransitionMode(e){if(!["immediate","next-downbeat","next-measure"].includes(e))throw new Error(`Invalid transition mode: ${e}`);const t=this.options.transitionMode;t!==e&&(J.debug("Transition mode change requested",{from:t,to:e}),this.options={...this.options,transitionMode:e},e==="immediate"&&this.state.pendingSubdivision&&(J.debug("Applying pending subdivision immediately due to mode switch",{pendingSubdivision:this.state.pendingSubdivision}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision)),J.debug("Transition mode changed",{from:t,to:e}))}applySubdivisionChange(e,t){this.state.currentSubdivision=e,this.state.pendingSubdivision=null,this.state.pendingMeasureNumber=null;const a=this.state.transitionTimestamp;if(this.state.transitionTimestamp=null,this.regenerateBeats(a),this.options.onSubdivisionChange)try{this.options.onSubdivisionChange(t,e)}catch(i){J.error("Error in onSubdivisionChange callback",i)}J.debug("Subdivision changed",{from:t,to:e,transitionTimestamp:a})}regenerateBeats(e=null){const t={beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},a=this.subdivider.subdivide(this.unifiedMap,t),i=new Map;if(e!==null)for(const[n,r]of this.state.scheduledBeats)r.beat.timestamp<e&&i.set(n,r);this.state.scheduledBeats.clear();for(const[n,r]of i)this.state.scheduledBeats.set(n,r);for(const n of a.beats){if(e!==null&&n.timestamp<e)continue;const r=this.getBeatKey(n);this.state.scheduledBeats.has(r)||this.state.scheduledBeats.set(r,{beat:n,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1})}J.debug("Beats regenerated",{subdivision:this.state.currentSubdivision,beatCount:a.beats.length,preservedBeatCount:i.size,totalScheduledBeats:this.state.scheduledBeats.size,fromTimestamp:e})}getBeatKey(e){return`${e.timestamp.toFixed(6)}-${e.subdivisionType}`}play(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate(),J.debug("Playback started",{startTime:this.state.startTime,pauseTime:this.state.pauseTime}))}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.regenerateBeats(),J.debug("Playback stopped")}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),J.debug("Playback paused",{pauseTime:this.state.pauseTime}))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate(),J.debug("Playback resumed",{startTime:this.state.startTime}))}seek(e){const t=Math.max(0,Math.min(e,this.unifiedMap.duration));this.state.pauseTime=t,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-t),this.regenerateBeats(),J.debug("Seeked to time",{time:t})}getBeatsInRange(e,t){const a=[];for(const[,i]of this.state.scheduledBeats){const n=i.beat;n.timestamp>=e&&n.timestamp<=t&&a.push(n)}return a.sort((i,n)=>i.timestamp-n.timestamp),a}getUpcomingBeats(e){const t=this.getCurrentAudioTime(),a=[];for(const[,i]of this.state.scheduledBeats){const n=i.beat,r=n.timestamp-t;if(r>=0&&r<=this.options.anticipationTime&&(a.push(n),a.length>=e))break}return a.sort((i,n)=>i.timestamp-n.timestamp),a}getBeatAtTime(e){const t=this.options.timingTolerance;for(const[,a]of this.state.scheduledBeats)if(Math.abs(a.beat.timestamp-e)<=t)return a.beat;return null}getCurrentBeat(){const e=this.getCurrentAudioTime();let t=null;for(const[,a]of this.state.scheduledBeats)if(a.beat.timestamp<=e)t=a.beat;else break;return t}getNextBeat(){const e=this.getCurrentAudioTime(),t=Array.from(this.state.scheduledBeats.values()).map(a=>a.beat).sort((a,i)=>a.timestamp-i.timestamp);for(const a of t)if(a.timestamp>e)return a;return null}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let e=0;if(this.options.compensateOutputLatency){const t=this.audioContext.outputLatency??0,a=this.audioContext.baseLatency??0;e+=t+a}return e+=this.options.userOffsetMs/1e3,e}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const e=this.getCurrentAudioTime();this.checkPendingSubdivisionChange(e);for(const t of this.state.scheduledBeats.values()){const i=t.beat.timestamp-e;!t.upcomingEmitted&&i<=this.options.anticipationTime&&i>0&&(this.emitEvent(t.beat,"upcoming",e,i),t.upcomingEmitted=!0),!t.exactEmitted&&Math.abs(i)<=this.options.timingTolerance&&(this.emitEvent(t.beat,"exact",e,i),t.exactEmitted=!0),!t.passedEmitted&&i<-this.options.timingTolerance&&(this.emitEvent(t.beat,"passed",e,i),t.passedEmitted=!0)}}checkPendingSubdivisionChange(e){if(!this.state.pendingSubdivision)return;const t=this.getCurrentBeat();if(!t)return;let a=!1;if(this.options.transitionMode==="next-downbeat")a=t.isDownbeat;else if(this.options.transitionMode==="next-measure"){const i=this.state.pendingMeasureNumber;i!==null?a=t.measureNumber>i&&Math.floor(t.beatInMeasure)===0:a=t.beatInMeasure===0}a&&(this.state.transitionTimestamp=t.timestamp,J.debug("Applying pending subdivision change",{newSubdivision:this.state.pendingSubdivision,transitionMode:this.options.transitionMode,currentMeasureNumber:t.measureNumber,pendingMeasureNumber:this.state.pendingMeasureNumber,beatInMeasure:t.beatInMeasure,transitionTimestamp:this.state.transitionTimestamp}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision))}emitEvent(e,t,a,i){const n={beat:e,currentSubdivision:this.state.currentSubdivision,audioTime:a,timeUntilBeat:i,type:t};for(const r of this.subscribers)try{r(n)}catch(o){J.error("SubdivisionPlaybackController callback error:",o)}}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.unifiedMap.duration}checkButtonPress(e,t){const a=t??this.state.thresholds,i=Array.from(this.state.scheduledBeats.values()).map(u=>u.beat);let n=null,r=1/0;for(const u of i){const d=e-u.timestamp,h=Math.abs(d);h<r&&(r=h,n=u)}if(!n)return{accuracy:"miss",offset:e,matchedBeat:null,absoluteOffset:e,keyMatch:!0,pressedKey:void 0,requiredKey:void 0};const o=e-n.timestamp,l=Math.abs(o);let c;return l<=a.perfect?c="perfect":l<=a.great?c="great":l<=a.good?c="good":l<=a.ok?c="ok":c="miss",{accuracy:c,offset:o,matchedBeat:n,absoluteOffset:l,keyMatch:!0,pressedKey:void 0,requiredKey:void 0}}setBeatMap(e){this.unifiedMap=e,this.regenerateBeats(),J.debug("Beat map updated",{audioId:e.audioId,beatCount:e.beats.length})}forceUpdate(){this.update()}getSubdividedBeatMap(){const e=[];for(const[,i]of this.state.scheduledBeats)e.push(i.beat);e.sort((i,n)=>i.timestamp-n.timestamp);const t=new Set(e.map(i=>i.subdivisionType)),a=e.filter(i=>i.isDetected).length;return{audioId:this.unifiedMap.audioId,duration:this.unifiedMap.duration,beats:e,detectedBeatIndices:[],subdivisionConfig:{beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},downbeatConfig:this.unifiedMap.downbeatConfig,subdivisionMetadata:{originalBeatCount:a,subdividedBeatCount:e.length,averageDensityMultiplier:e.length/Math.max(1,this.unifiedMap.beats.length),explicitBeatCount:0,subdivisionsUsed:Array.from(t),hasMultipleTempos:!1,maxDensity:Math.max(...Array.from(t).map(i=>i==="eighth"?2:i==="sixteenth"?4:i==="triplet8"||i==="triplet4"?3:1))}}}dispose(){this.stop(),this.subscribers.clear(),this.state.scheduledBeats.clear(),J.debug("SubdivisionPlaybackController disposed")}}const Wh=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],zh={stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5};class ti{constructor(e){this.config={level_thresholds:e?.level_thresholds||Wh,xp_per_second:e?.xp_per_second??1,xp_per_track_completion:e?.xp_per_track_completion??50,activity_bonuses:{...zh,...e?.activity_bonuses},track_mastery_threshold:e?.track_mastery_threshold??10,mastery_bonus_xp:e?.mastery_bonus_xp??100}}calculateSessionXP(e,t){let a=e.duration_seconds*this.config.xp_per_second;if(e.activity_type&&e.activity_type in this.config.activity_bonuses){const i=this.config.activity_bonuses[e.activity_type];a*=i}return e.environmental_context&&(a=this.applyEnvironmentalBonus(a,e.environmental_context)),e.gaming_context&&(a=this.applyGamingBonus(a,e.gaming_context)),e.rhythm_game_context&&(a=this.applyRhythmGameBonus(a,e.rhythm_game_context)),t&&e.duration_seconds>=t.duration*.95&&(a+=this.config.xp_per_track_completion),Math.floor(a)}applyEnvironmentalBonus(e,t){let a=1;return t.weather?.isNight&&(a*=this.config.activity_bonuses.night_time),t.weather&&(t.weather.weatherType==="Thunderstorm"&&(a*=this.config.activity_bonuses.extreme_weather),(t.weather.weatherType==="Rain"||t.weather.weatherType==="Snow")&&(a*=this.config.activity_bonuses.extreme_weather)),t.geolocation?.altitude&&t.geolocation.altitude>=2e3&&(a*=this.config.activity_bonuses.high_altitude),e*a}applyGamingBonus(e,t){if(!t.isActivelyGaming)return e;let a=1;if(a+=.25,t.currentGame){if(t.currentGame.genre){const i=t.currentGame.genre;(i.includes("Action")||i.includes("FPS"))&&(a+=.15),i.includes("RPG")&&(a+=.2),i.includes("Strategy")&&(a+=.1)}if(t.currentGame.partySize&&t.currentGame.partySize>1&&(a+=.15),t.currentGame.sessionDuration){const i=t.currentGame.sessionDuration/60;i>=1&&(a+=Math.min(.2,i*.05))}}return e*a}applyRhythmGameBonus(e,t){if(!t.isActive)return e;let a=this.config.activity_bonuses.rhythm_game_base;if(t.currentCombo>0){const i=t.currentCombo/t.maxComboCap;a+=i*this.config.activity_bonuses.rhythm_game_combo}if(t.grooveHotness>0){const i=t.grooveHotness/100;a+=i*this.config.activity_bonuses.rhythm_game_groove}return e*a}calculateTotalModifier(e,t,a){let i=1;return e&&(i*=this.calculateEnvironmentalModifier(e)),t&&t.isActivelyGaming&&(i*=this.calculateGamingModifier(t)),a&&a.isActive&&(i*=this.calculateRhythmGameModifier(a)),Math.min(i,3)}calculateEnvironmentalModifier(e){let t=1;return e.weather?.weatherType==="Thunderstorm"&&(t*=this.config.activity_bonuses.extreme_weather),e.weather?.isNight&&(t*=this.config.activity_bonuses.night_time),e.geolocation?.altitude&&e.geolocation.altitude>=2e3&&(t*=this.config.activity_bonuses.high_altitude),Math.min(t,3)}calculateGamingModifier(e){let t=1;if(t+=.25,e.currentGame){if(e.currentGame.genre){const a=e.currentGame.genre;(a.includes("Action")||a.includes("FPS"))&&(t+=.15),a.includes("RPG")&&(t+=.2),a.includes("Strategy")&&(t+=.1)}if(e.currentGame.partySize&&e.currentGame.partySize>1&&(t+=.15),e.currentGame.sessionDuration){const a=e.currentGame.sessionDuration/60;a>=1&&(t+=Math.min(.2,a*.05))}}return Math.min(t,1.75)}calculateRhythmGameModifier(e){let t=this.config.activity_bonuses.rhythm_game_base;if(e.currentCombo>0){const a=e.currentCombo/e.maxComboCap;t+=a*this.config.activity_bonuses.rhythm_game_combo}if(e.grooveHotness>0){const a=e.grooveHotness/100;t+=a*this.config.activity_bonuses.rhythm_game_groove}return Math.min(t,2.25)}getXPThresholdForLevel(e){if(e<1||e>20)throw new Error("Level must be between 1 and 20");return this.config.level_thresholds[e-1]}getXPToNextLevel(e){if(e<1||e>19)throw new Error("Current level must be between 1 and 19");const t=this.config.level_thresholds[e-1];return this.config.level_thresholds[e]-t}getLevelFromXP(e){for(let t=20;t>=1;t--)if(e>=this.config.level_thresholds[t-1])return t;return 1}isTrackMastered(e){return e>=this.config.track_mastery_threshold}getMasteryBonusXP(){return this.config.mastery_bonus_xp}getConfig(){return{...this.config}}}class qh{constructor(e){this.activeSessions=new Map,this.sessionHistory=[],this.sessionCounter=0,this.xpCalculator=e||new ti}startSession(e,t,a){const i=`session-${this.sessionCounter++}-${e}-${Date.now()}`;return this.activeSessions.set(i,{track_uuid:e,start_time:Date.now(),track:t,environmental_context:a?.environmental_context,gaming_context:a?.gaming_context}),i}endSession(e,t,a){const i=this.activeSessions.get(e);if(!i)return null;const n=Date.now(),r=t||Math.max(1,Math.ceil((n-i.start_time)/1e3)),o={track_uuid:i.track_uuid,start_time:i.start_time,end_time:n,duration_seconds:Math.round(r),base_xp_earned:0,bonus_xp:0,activity_type:a,environmental_context:i.environmental_context,gaming_context:i.gaming_context,total_xp_earned:0};return o.base_xp_earned=Math.floor(o.duration_seconds*this.xpCalculator.getConfig().xp_per_second),o.total_xp_earned=this.xpCalculator.calculateSessionXP(o,i.track),o.bonus_xp=o.total_xp_earned-o.base_xp_earned,this.sessionHistory.push(o),this.activeSessions.delete(e),o}getActiveSession(e){return this.activeSessions.get(e)||null}getActiveSessionDuration(e){const t=this.activeSessions.get(e);return t?(Date.now()-t.start_time)/1e3:null}updateSessionContext(e,t){const a=this.activeSessions.get(e);return a?(t.environmental_context&&(a.environmental_context=t.environmental_context),t.gaming_context&&(a.gaming_context=t.gaming_context),!0):!1}getSessionHistory(){return[...this.sessionHistory]}getSessionsForTrack(e){return this.sessionHistory.filter(t=>t.track_uuid===e)}getTotalListeningTime(){return this.sessionHistory.reduce((e,t)=>e+t.duration_seconds,0)}getTotalXPEarned(){return this.sessionHistory.reduce((e,t)=>e+t.total_xp_earned,0)}getTrackListeningTime(e){return this.getSessionsForTrack(e).reduce((t,a)=>t+a.duration_seconds,0)}getTrackListenCount(e){return this.getSessionsForTrack(e).length}isTrackMastered(e,t=10){return this.getTrackListenCount(e)>=t}getSessionsInRange(e,t){return this.sessionHistory.filter(a=>a.start_time>=e&&a.end_time<=t)}getAverageSessionLength(){return this.sessionHistory.length===0?0:this.getTotalListeningTime()/this.sessionHistory.length}getLongestSession(){return this.sessionHistory.length===0?null:this.sessionHistory.reduce((e,t)=>t.duration_seconds>e.duration_seconds?t:e)}getTrackXPTotal(e){return this.getSessionsForTrack(e).reduce((t,a)=>t+a.total_xp_earned,0)}clearTrackSessions(e){const t=this.sessionHistory.length;return this.sessionHistory=this.sessionHistory.filter(a=>a.track_uuid!==e),t-this.sessionHistory.length}clearHistory(){this.sessionHistory=[]}clearActiveSessions(){this.activeSessions.clear()}getActiveSessionCount(){return this.activeSessions.size}getActiveSessionIds(){return Array.from(this.activeSessions.keys())}}const ns=[4,8,12,16,19];class ke{static setUncappedConfig(e){this.uncappedConfig=e}static getUncappedConfig(){return this.uncappedConfig}static setStatManager(e){this.statManager=e}static getStatManager(){return this.statManager}static processLevelUp(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=n?1/0:20;if(t<1||t>r)throw new Error(`Level must be between 1 and ${r}`);const o=Ce[e.class];if(!o)throw new Error(`Unknown class: ${e.class}`);const l=this.calculateHPIncrease(o.hit_die,e.ability_modifiers.CON,a),c=e.hp.max+l,u=this.getProficiencyBonus(t,n),d=u-e.proficiency_bonus,h={newLevel:t,hitPointIncrease:l,newHitPointsTotal:c,proficiencyBonusIncrease:d,newProficiencyBonus:u},p=n||ns.includes(t);if(this.statManager&&p){const m=this.statManager.processLevelUp(e,t);m&&m.increases.length>0&&(h.abilityScoreIncreases=m.increases.map(f=>({ability:f.ability,increase:f.delta})),m.increases.length===1&&(h.abilityScoreIncrease={ability:m.increases[0].ability,increase:m.increases[0].delta}))}else ns.includes(t)&&(h.abilityScoreIncrease={ability:"STR",increase:2});if(this.isSpellcaster(e.class)){const m=this.calculateSpellSlots(t),f={};for(const[v,w]of Object.entries(m))f[Number(v)]={total:w,used:0};h.newSpellSlots=f}const g=this.getClassFeaturesForLevel(e,e.class,t);if(g.length>0){h.classFeatures=g.map(m=>m.id),h.featureEffects=[];for(const m of g)if(m.effects&&m.effects.length>0){const f={...e,level:t},v=ht.applyFeatureEffects(f,m);v.applied&&h.featureEffects.push({featureId:m.id,featureName:m.name,effectsApplied:v.count})}}return h}static getClassFeaturesForLevel(e,t,a){const i=re.getInstance(),n=i.getFeaturesForLevel(t,a),r={...e,level:a},o=[];for(const l of n){const c=i.validatePrerequisites(l,r);c.valid?o.push(l):(console.warn(`Feature "${l.name}" (level ${l.level}) failed prerequisite validation at level ${a}:`,c.errors),l.source==="default"&&o.push(l))}return o}static applyLevelUp(e,t){const a={...e};a.level=t.newLevel,a.hp.max=t.newHitPointsTotal,a.hp.current=Math.min(a.hp.current+t.hitPointIncrease,a.hp.max),a.proficiency_bonus=t.newProficiencyBonus;const i=a.gameMode==="uncapped"?1/0:20;if(t.abilityScoreIncreases&&t.abilityScoreIncreases.length>0)for(const n of t.abilityScoreIncreases){const r=n.ability;a.ability_scores[r]=Math.min(i,a.ability_scores[r]+n.increase);const o=a.ability_scores[r];a.ability_modifiers[r]=Math.floor((o-10)/2)}else if(t.abilityScoreIncrease){const n=t.abilityScoreIncrease.ability;a.ability_scores[n]=Math.min(i,a.ability_scores[n]+t.abilityScoreIncrease.increase);const r=a.ability_scores[n];a.ability_modifiers[n]=Math.floor((r-10)/2)}return t.newSpellSlots&&a.spells&&(a.spells.spell_slots=t.newSpellSlots),t.classFeatures&&(a.class_features=[...new Set([...a.class_features,...t.classFeatures])]),this.reapplyEquipmentEffects(a),a}static calculateHPIncrease(e,t,a){let i;return a?i=new Se(a).randomInt(1,e+1):i=Math.floor(Math.random()*e)+1,Math.max(1,i+t)}static isSpellcaster(e){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(e)}static calculateSpellSlots(e){const t={1:0};for(let a=1;a<=9;a++)t[a]=this.getSpellSlotCount(e,a);return t}static getSpellSlotCount(e,t){return t>Math.ceil(e/2)||e<1?0:e<3?t===1?2:0:e<5?t===1?3:t===2?2:0:e<7?t===1?4:t===2?3:0:e<9?t===1||t===2?4:t===3?2:0:Math.min(5,Math.ceil(e/2))}static getXPThreshold(e,t=!1){if(e<1)throw new Error("Level must be at least 1");if(!t){if(e>20)throw new Error("Standard mode only supports levels 1-20");return ct[e]}if(this.uncappedConfig?.xpFormula)return this.uncappedConfig.xpFormula(e);if(e<=20)return ct[e];let a=355e3;for(let i=21;i<=e;i++)a+=(i-1)*i*500;return a}static getProficiencyBonus(e,t=!1){if(!t){if(e<1||e>20)throw new Error("Standard proficiency bonus only defined for levels 1-20");return Ma[e]}return this.uncappedConfig?.proficiencyBonusFormula?this.uncappedConfig.proficiencyBonusFormula(e):e<=4?2:e<=8?3:e<=12?4:e<=16?5:2+Math.floor((e-1)/4)}static calculateLevel(e,t=!1){if(!t){for(let i=20;i>=1;i--)if(e>=this.getXPThreshold(i,!1))return i;return 1}let a=1;for(;e>=this.getXPThreshold(a+1,!0);)a++;return a}static getXPToNextLevel(e,t=!1){if(!t&&e>=20)return 0;const a=this.getXPThreshold(e,t);return this.getXPThreshold(e+1,t)-a}static getProgressPercentage(e,t,a=!1){if(!a&&e>=20)return 100;const i=this.getXPThreshold(e,a),n=this.getXPThreshold(e+1,a),r=t-i,o=n-i;return Math.floor(r/o*100)}static processLevelUpWithoutStats(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=n?1/0:20;if(t<1||t>r)throw new Error(`Level must be between 1 and ${r}`);const o=Ce[e.class];if(!o)throw new Error(`Unknown class: ${e.class}`);const l=this.calculateHPIncrease(o.hit_die,e.ability_modifiers.CON,a),c=e.hp.max+l,u=this.getProficiencyBonus(t,n),d=u-e.proficiency_bonus,h={newLevel:t,hitPointIncrease:l,newHitPointsTotal:c,proficiencyBonusIncrease:d,newProficiencyBonus:u};if(this.isSpellcaster(e.class)){const g=this.calculateSpellSlots(t),m={};for(const[f,v]of Object.entries(g))m[Number(f)]={total:v,used:0};h.newSpellSlots=m}const p=this.getClassFeaturesForLevel(e,e.class,t);if(p.length>0){h.classFeatures=p.map(g=>g.id),h.featureEffects=[];for(const g of p)if(g.effects&&g.effects.length>0){const m={...e,level:t},f=ht.applyFeatureEffects(m,g);f.applied&&h.featureEffects.push({featureId:g.id,featureName:g.name,effectsApplied:f.count})}}return h}static applyAutomaticBenefitsOnly(e,t){const a={...e};return a.level=t.newLevel,a.hp.max=t.newHitPointsTotal,a.hp.current=Math.min(a.hp.current+t.hitPointIncrease,a.hp.max),a.proficiency_bonus=t.newProficiencyBonus,t.newSpellSlots&&a.spells&&(a.spells.spell_slots=t.newSpellSlots),t.classFeatures&&(a.class_features=[...new Set([...a.class_features,...t.classFeatures])]),this.reapplyEquipmentEffects(a),a}static applyStatIncreasesOnly(e,t){const a={...e};a.ability_scores={...a.ability_scores},a.ability_modifiers={...a.ability_modifiers};const n=(a.gameMode||"standard")==="uncapped"?1/0:20;for(const r of t){const o=r.ability;a.ability_scores[o]=Math.min(n,a.ability_scores[o]+r.amount),a.ability_modifiers[o]=Math.floor((a.ability_scores[o]-10)/2)}return a}static reapplyEquipmentEffects(e){e.equipment&&le.reapplyEquipmentEffects(e)}}class Ln{constructor(){this.name="D&D 5e Standard"}selectIncreases(e,t,a){if(a?.forcedAbilities&&a.forcedAbilities.length>0){const i=[];for(const n of a.forcedAbilities)e.ability_scores[n]>=20||i.push({ability:n,amount:t/a.forcedAbilities.length});return i}return[]}requiresManualInput(e){return!e?.forcedAbilities||e.forcedAbilities.length===0}}class On{constructor(){this.name="D&D 5e Smart"}selectIncreases(e,t,a){const i=new Set(a?.excludedAbilities||[]),n=["STR","DEX","CON","INT","WIS","CHA"].filter(c=>!i.has(c)&&e.ability_scores[c]<20);if(n.length===0)return[];const o=Ce[e.class]?.primary_ability||"STR";if(!i.has(o)&&e.ability_scores[o]<18&&n.includes(o)){if(t===2&&this.shouldSplit(e,o)){const c=this.findLowestStat(e,n,o);return[{ability:o,amount:1},{ability:c,amount:1}]}return[{ability:o,amount:t}]}if(t===2&&n.length>=2){const c=this.findLowestStat(e,n),u=this.findLowestStat(e,n.filter(d=>d!==c));return[{ability:c,amount:1},{ability:u,amount:1}]}return[{ability:this.findLowestStat(e,n),amount:t}]}requiresManualInput(){return!1}shouldSplit(e,t){const a=e.ability_scores[t];if(a<15)return!1;const i=Object.values(e.ability_scores);return Math.min(...i)<a-4}findLowestStat(e,t,a){let i=t[0],n=e.ability_scores[i];for(const r of t){if(r===a)continue;const o=e.ability_scores[r];o<n&&(i=r,n=o)}return i}}class Bn{constructor(){this.name="Balanced"}selectIncreases(e,t,a){const i=new Set(a?.excludedAbilities||[]),n=["STR","DEX","CON","INT","WIS","CHA"].filter(l=>!i.has(l)).filter(l=>e.ability_scores[l]<20).sort((l,c)=>e.ability_scores[l]-e.ability_scores[c]);if(n.length===0)return[];if(a?.forcedAbilities&&a.forcedAbilities.length>=2)return[{ability:a.forcedAbilities[0],amount:1},{ability:a.forcedAbilities[1],amount:1}];const r=[];let o=t;for(let l=0;l<n.length&&o>0;l++)r.push({ability:n[l],amount:1}),o--;return r}requiresManualInput(){return!1}}class Fn{constructor(){this.name="Primary Only"}selectIncreases(e,t,a){const n=Ce[e.class]?.primary_ability||"STR";return a?.excludedAbilities?.includes(n)?[{ability:this.findLowestStat(e,a?.excludedAbilities),amount:t}]:e.ability_scores[n]>=20?[{ability:this.findLowestStat(e,[n]),amount:t}]:[{ability:n,amount:t}]}requiresManualInput(){return!1}findLowestStat(e,t){const a=["STR","DEX","CON","INT","WIS","CHA"].filter(r=>!t?.includes(r)).filter(r=>e.ability_scores[r]<20);if(a.length===0)return"STR";let i=a[0],n=e.ability_scores[i];for(const r of a){const o=e.ability_scores[r];o<n&&(i=r,n=o)}return i}}class Hn{constructor(){this.name="Random"}selectIncreases(e,t,a){const i=["STR","DEX","CON","INT","WIS","CHA"].filter(r=>!a?.excludedAbilities?.includes(r)).filter(r=>e.ability_scores[r]<20);if(i.length===0)return[];const n=[...i].sort(()=>Math.random()-.5);return t===2&&n.length>=2?[{ability:n[0],amount:1},{ability:n[1],amount:1}]:[{ability:n[0],amount:t}]}requiresManualInput(){return!1}}class Wn{constructor(){this.name="Manual"}selectIncreases(e,t,a){return[]}requiresManualInput(){return!0}}class Uh{constructor(e,t){this.fn=e,this.name=t||"Custom Function"}selectIncreases(e,t,a){return this.fn(e,t,a)}requiresManualInput(){return!1}}function _a(s){if(typeof s!="string"&&typeof s!="function")return s;if(typeof s=="function")return new Uh(s);switch(s){case"dnD5e":return new Ln;case"dnD5e_smart":return new On;case"balanced":return new Bn;case"primary_only":return new Fn;case"random":return new Hn;case"manual":return new Wn;default:const e=s;throw new Error(`Unknown strategy type: ${e}`)}}const rs=[4,8,12,16,19],os={strategy:"dnD5e",autoApply:!0};class zn{constructor(e){this.config=this.mergeWithDefaults(e),this.strategy=_a(this.config.strategy)}increaseStats(e,t,a="manual"){const i={...e},n=[],r=[];i.ability_scores={...i.ability_scores},i.ability_modifiers={...i.ability_modifiers};const l=(i.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;for(const{ability:c,amount:u}of t){const d=i.ability_scores[c],h=d+u,p=Math.min(h,l),g=p-d;g>0?(i.ability_scores[c]=p,i.ability_modifiers[c]=Math.floor((p-10)/2),n.push({ability:c,oldValue:d,newValue:p,delta:g})):h>l&&r.push({ability:c,attemptedValue:h,cappedAt:l})}return{character:i,increases:n,capped:r,source:a,timestamp:Date.now()}}decreaseStats(e,t,a="event"){const i=t.map(({ability:n,amount:r})=>({ability:n,amount:-r}));return this.increaseStats(e,i,a)}setStat(e,t,a,i="manual"){const n=e.ability_scores[t],r=a-n;return this.increaseStats(e,[{ability:t,amount:r}],i)}processLevelUp(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=this.config.statIncreaseLevels?.length?this.config.statIncreaseLevels:rs;if(!(n||r.includes(t)))return null;const c=this.strategy.selectIncreases(e,2,a);return c.length===0?null:this.increaseStats(e,c,"level_up")}updateConfig(e){const t=this.config.strategy;this.config=this.mergeWithDefaults(e),e.strategy&&e.strategy!==t&&(this.strategy=_a(this.config.strategy))}getConfig(){return this.config}canIncrease(e,t,a=1){const i=e.ability_scores[t],r=(e.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;return i+a<=r}getStatCap(e,t){return(e.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20}validateDnD5eStatSelection(e,t,a=2){const i=t.reduce((u,d)=>u+d.amount,0);if(i!==a)return{error:`Total stat increase must be ${a}, got ${i}`,reason:"invalid_amount",allowedPatterns:[`+${a} to one ability`,"+1 to two abilities"]};const n=[{count:1,amount:a},{count:2,amount:1}];if(!n.some(u=>t.length===u.count&&t.every(d=>d.amount===u.amount)))return{error:"Invalid pattern: must be +2 to one ability or +1 to two abilities",reason:"wrong_pattern",allowedPatterns:n.map(u=>u.count===1?`+${u.amount} to one ability`:"+1 to two abilities")};const o=this.getStatCap(e,"STR");for(const u of t){const d=e.ability_scores[u.ability];if(d+u.amount>o&&o!==1/0)return{error:`${u.ability} would exceed stat cap of ${o}`,reason:"exceeds_cap",allowedPatterns:[`Choose different abilities or use ${o-d} points`]}}const l=["STR","DEX","CON","INT","WIS","CHA"];for(const u of t)if(!l.includes(u.ability))return{error:`Invalid ability: ${u.ability}`,reason:"invalid_ability",allowedPatterns:l};const c=new Set;for(const u of t){if(c.has(u.ability))return{error:`Duplicate ability: ${u.ability}`,reason:"duplicate_ability",allowedPatterns:["Each ability can only be increased once"]};c.add(u.ability)}return{valid:!0}}mergeWithDefaults(e){return{maxStatCap:e?.maxStatCap??20,strategy:e?.strategy??os.strategy,autoApply:e?.autoApply??os.autoApply,statIncreaseLevels:e?.statIncreaseLevels??rs}}}const ui=class ui{static getPlaysThreshold(e){const t=this.customThresholds.get(e);return t?.playsThreshold!==void 0&&t.playsThreshold!==null?t.playsThreshold:Math.floor(hs*Math.pow(ia,e))}static getXPThreshold(e){const t=this.customThresholds.get(e);return t?.xpThreshold!==void 0&&t.xpThreshold!==null?t.xpThreshold:Math.floor(ps*Math.pow(ia,e))}static setCustomThresholds(e,t){this.customThresholds.set(e,{...t})}static clearCustomThresholds(e){e!==void 0?this.customThresholds.delete(e):this.customThresholds.clear()}static hasCustomThresholds(e){return this.customThresholds.has(e)}static getCustomThresholds(e){return this.customThresholds.get(e)}static isMastered(e,t,a){const i=this.getPlaysThreshold(a),n=this.getXPThreshold(a);return e>=i&&t>=n}static canPrestige(e,t,a){return e>=st?!1:this.isMastered(t,a,e)}static isJustMastered(e,t,a,i,n){const r=this.isMastered(e,a,n),o=this.isMastered(t,i,n);return!r&&o}static calculateMasteryBonus(e){return e?xs:0}static getPrestigeInfo(e,t,a){const i=this.getPlaysThreshold(e),n=this.getXPThreshold(e),r=this.isMastered(t,a,e),o=e>=st;return{prestigeLevel:e,currentPlays:t,currentXP:a,playsThreshold:i,xpThreshold:n,playsProgress:Math.min(1,t/i),xpProgress:Math.min(1,a/n),isMastered:r,canPrestige:r&&!o,isMaxPrestige:o}}static toRomanNumeral(e){return ds[e]}static getNextPrestigeLevel(e){return e>=st?null:ms(e+1)}static createSuccessResult(e,t){const a=this.toRomanNumeral(t);return{success:!0,newPrestigeLevel:t,previousPrestigeLevel:e,message:`Successfully prestiged to level ${a}! New mastery requirements: ${this.getPlaysThreshold(t)} plays, ${this.getXPThreshold(t).toLocaleString()} XP`}}static createFailureResult(e,t){return{success:!1,newPrestigeLevel:t,previousPrestigeLevel:t,message:`Prestige failed: ${e}`}}static getAllThresholds(){const e=[];for(let t=0;t<=st;t++){const a=t;e.push({level:a,plays:this.getPlaysThreshold(a),xp:this.getXPThreshold(a)})}return e}};ui.customThresholds=new Map;let se=ui;class Gh{constructor(e){this.xpCalculator=new ti,this.statManager=e,this.statManager&&ke.setStatManager(this.statManager)}addXP(e,t,a="custom"){const i={...e,xp:{...e.xp}};if(i.xp.current+=t,!this.statManager){const d=(i.gameMode||"standard")==="standard"?"dnD5e":"dnD5e_smart";this.statManager=new zn({strategy:d}),ke.setStatManager(this.statManager)}let n=!1,r;const o=[],l=i.gameMode==="uncapped",c=ke.calculateLevel(i.xp.current,l);if(c>i.level){n=!0,r=c;for(let u=i.level+1;u<=c;u++)if(this.isStatIncreaseLevel(u,l)&&this.statManager.getConfig().strategy==="dnD5e"){i.pendingStatIncreases=(i.pendingStatIncreases||0)+1;const p=ke.processLevelUpWithoutStats(i,u),g=ke.applyAutomaticBenefitsOnly(i,p),m={fromLevel:u-1,toLevel:u,hpIncrease:p.hitPointIncrease,newMaxHP:p.newHitPointsTotal,proficiencyIncrease:p.proficiencyBonusIncrease,newProficiency:p.newProficiencyBonus,statIncreases:void 0,featuresGained:p.classFeatures,newSpellSlots:p.newSpellSlots};o.push(m),i.level=u,i.hp=g.hp,i.proficiency_bonus=g.proficiency_bonus,i.class_features=g.class_features,g.spells&&(i.spells=g.spells)}else{const p={...i.ability_scores},g=[...i.class_features],m=ke.processLevelUp(i,u),f=ke.applyLevelUp(i,m),v={fromLevel:u-1,toLevel:u,hpIncrease:m.hitPointIncrease,newMaxHP:m.newHitPointsTotal,proficiencyIncrease:m.proficiencyBonusIncrease,newProficiency:m.newProficiencyBonus,statIncreases:m.abilityScoreIncreases?.map(w=>({ability:w.ability,oldValue:p[w.ability],newValue:p[w.ability]+w.increase,delta:w.increase})),featuresGained:m.classFeatures?.filter(w=>!g.includes(w)),newSpellSlots:m.newSpellSlots};o.push(v),i.level=f.level,i.hp=f.hp,i.ability_scores=f.ability_scores,i.ability_modifiers=f.ability_modifiers,i.proficiency_bonus=f.proficiency_bonus,i.class_features=f.class_features,f.spells&&(i.spells=f.spells)}}return!l&&i.level>=20?i.xp.next_level=0:i.xp.next_level=ke.getXPThreshold(i.level+1,l),{character:i,xpEarned:t,leveledUp:n,newLevel:r,levelUpDetails:o.length>0?o:void 0}}addRhythmXP(e,t,a="rhythm_game"){const i=this.addXP(e,t.finalXP,a);return{character:i.character,xpEarned:i.xpEarned,leveledUp:i.leveledUp,newLevel:i.newLevel,levelUpDetails:i.levelUpDetails}}isStatIncreaseLevel(e,t){return t?!0:[4,8,12,16,19].includes(e)}updateCharacterFromSession(e,t,a,i){const n=i?.previousListenCount??0,r=i?.previousXP??0,o=i?.prestigeLevel??e.prestige_level??0,l=this.xpCalculator.calculateSessionXP(t,a);let c=!1,u=0;if(a){const p=n+1,g=r+l;se.isJustMastered(n,p,r,g,o)&&(c=!0,u=se.calculateMasteryBonus(!0))}const d=l+u;return{...this.addXP(e,d,"listening"),masteredTrack:c,masteryBonusXP:u}}applyPendingStatIncrease(e,t,a){const i=e.pendingStatIncreases||0;if(i===0)throw new Error("No pending stat increases to apply");let n;if(a&&a.length>0){if(a.length>1)throw new Error("Only one secondary stat allowed for +1/+1 distribution");n=[{ability:t,amount:1},{ability:a[0],amount:1}]}else n=[{ability:t,amount:2}];const r=this.statManager.validateDnD5eStatSelection(e,n);if("error"in r)throw new Error(r.error);const o=ke.applyStatIncreasesOnly(e,n);o.pendingStatIncreases=i-1,o.pendingStatIncreases===0&&delete o.pendingStatIncreases;const l=n.map(c=>({ability:c.ability,oldValue:e.ability_scores[c.ability],newValue:o.ability_scores[c.ability],delta:c.amount}));return{character:o,statIncreases:l,remainingPending:o.pendingStatIncreases||0,timestamp:Date.now()}}hasPendingStatIncreases(e){return!!(e.pendingStatIncreases&&e.pendingStatIncreases>0)}getPendingStatIncreaseCount(e){return e.pendingStatIncreases||0}resetCharacterForPrestige(e,t,a,i,n){const r=e.prestige_level??0;if(r>=10)return se.createFailureResult("Already at maximum prestige level",r);const o=t.getTrackListenCount(a),l=t.getTrackXPTotal(a);if(!se.canPrestige(r,o,l)){const p=se.getPlaysThreshold(r),g=se.getXPThreshold(r);return o<p&&l<g?se.createFailureResult(`Need ${p-o} more plays and ${(g-l).toLocaleString()} more XP to prestige`,r):o<p?se.createFailureResult(`Need ${p-o} more plays to prestige (XP requirement met)`,r):se.createFailureResult(`Need ${(g-l).toLocaleString()} more XP to prestige (plays requirement met)`,r)}const c=e.equipment?{weapons:[...e.equipment.weapons],armor:[...e.equipment.armor],items:[...e.equipment.items],totalWeight:e.equipment.totalWeight,equippedWeight:e.equipment.equippedWeight}:null;t.clearTrackSessions(a);const u=se.getNextPrestigeLevel(r);if(u===null)return se.createFailureResult("Cannot prestige beyond maximum level",r);const d=Vt.generate(e.seed,i,n,{level:1,gameMode:e.gameMode,forceName:e.name});return c&&(d.equipment=c),d.prestige_level=u,{...se.createSuccessResult(r,u),character:d}}canPrestige(e,t,a){const i=e.prestige_level??0,n=t.getTrackListenCount(a),r=t.getTrackXPTotal(a);return se.canPrestige(i,n,r)}getPrestigeInfo(e,t,a){const i=e.prestige_level??0,n=t.getTrackListenCount(a),r=t.getTrackXPTotal(a);return se.getPrestigeInfo(i,n,r)}}class jh{constructor(e){this.sessionTotals=null,this.sessionStartTime=null,this.config=Pt(e)}calculateButtonPressXP(e,t){const a=t?.comboLength??0,i=t?.grooveHotness,n=this.getBaseXP(e),r=n*this.config.xpRatio,o=this.config.combo.enabled?this.getComboMultiplier(a):1;let l=0;this.config.groove.perHitMultiplier&&i!==void 0&&i>0&&(l=i/100*this.config.groove.perHitScale);const c=Math.min(o+l,this.config.maxMultiplier),u=n*c,d=r*c,h=Math.max(0,d);return{scorePoints:n,baseXP:r,comboMultiplier:o,grooveMultiplier:l,totalMultiplier:c,finalScore:u,finalXP:h,breakdown:{accuracy:e,comboLength:a,grooveHotness:i}}}calculateComboEndBonus(e){if(!this.config.combo.endBonus.enabled||e<=0)return{comboLength:e,bonusScore:0,bonusXP:0};const t=this.config.combo.endBonus.formula?this.config.combo.endBonus.formula(e):e*2,a=t*this.config.xpRatio;return{comboLength:e,bonusScore:t,bonusXP:a}}calculateGrooveEndBonus(e){if(!this.config.groove.endBonus.enabled)return{bonusScore:0,bonusXP:0};if(e.maxStreak<2)return{bonusScore:0,bonusXP:0};const{maxStreakWeight:a,avgHotnessWeight:i,durationWeight:n}=this.config.groove.endBonus,r=e.maxStreak*a,o=e.avgHotness*i,l=e.duration*n,c=r+o+l,u=c*this.config.xpRatio;return{bonusScore:c,bonusXP:u}}getBaseXP(e){return this.config.baseXP[e]}getComboMultiplier(e){if(!this.config.combo.enabled)return 1;const t=this.config.combo.formula?this.config.combo.formula(e):this.defaultComboFormula(e);return Math.min(t,this.config.combo.cap)}defaultComboFormula(e){return 1+e/25}startSession(){this.sessionTotals=this.createEmptyTotals(),this.sessionStartTime=Date.now()}recordHit(e,t){this.sessionTotals||this.startSession();const a=this.calculateButtonPressXP(e,t);this.sessionTotals.totalScore+=a.finalScore,this.sessionTotals.totalXP+=a.finalXP;const i=t?.comboLength??0;return i>this.sessionTotals.maxCombo&&(this.sessionTotals.maxCombo=i),this.sessionTotals.accuracyDistribution[e]++,this.recalculateAccuracyPercentage(),a}getSessionTotals(){return!this.sessionTotals||!this.sessionStartTime?null:{...this.sessionTotals,duration:(Date.now()-this.sessionStartTime)/1e3}}endSession(){const e=this.getSessionTotals();return this.sessionTotals=null,this.sessionStartTime=null,e}createEmptyTotals(){return{totalScore:0,totalXP:0,maxCombo:0,accuracyDistribution:{perfect:0,great:0,good:0,ok:0,miss:0,wrongKey:0},accuracyPercentage:0,duration:0}}recalculateAccuracyPercentage(){if(!this.sessionTotals)return;const{perfect:e,great:t,good:a,ok:i,miss:n,wrongKey:r}=this.sessionTotals.accuracyDistribution,o=e+t+a+i+n+r;if(o===0){this.sessionTotals.accuracyPercentage=0;return}const l=e+t+a+i;this.sessionTotals.accuracyPercentage=l/o*100}getConfig(){return this.config}updateConfig(e){this.config=Pt({...this.config,...e})}}class Vh{static enchant(e,t,a,i){return Dt.validateModification(a).valid?this.addModificationToEquipment(e,t,a,i):e}static applyTemplate(e,t,a,i){const o=R.getInstance().get("equipment.templates").find(d=>d.id===a);if(!o)return e;const l={id:`template_${a}_${Date.now()}`,name:o.name||`Template: ${a}`,properties:o.properties||[],addsFeatures:o.addsFeatures,addsSkills:o.addsSkills,addsSpells:o.addsSpells,appliedAt:new Date().toISOString(),source:"template"},c=this.addModificationToEquipment(e,t,l,i),u=this.findItem(c,t);return u&&(u.templateId=a),c}static curse(e,t,a,i){return Dt.validateModification(a).valid?this.addModificationToEquipment(e,t,a,i):e}static upgrade(e,t,a,i){return this.enchant(e,t,a,i)}static removeModification(e,t,a,i){const n={weapons:e.weapons.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),armor:e.armor.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),items:e.items.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight},r=this.findItem(n,t);if(!r||!r.modifications)return e;const o=r.equipped,l=r.instanceId,c=this.getEquipmentData(t);o&&i&&c&&le.unequipItem(i,t,l);const u=r.modifications.findIndex(d=>d.id===a);if(u!==-1&&r.modifications.splice(u,1),o&&i&&c){le.equipItem(i,c,l);for(const d of r.modifications)this.applyModificationEffects(i,c,d,l)}return n}static getModificationHistory(e,t){const a=this.findItem(e,t);return!a||!a.modifications?[]:a.modifications.map(i=>({...i}))}static getCombinedEffects(e,t,a){const i=this.getEquipmentData(t);if(!i)return[];const n=i.properties?[...i.properties]:[],r=this.findItem(e,t);if(r&&r.modifications)for(const o of r.modifications)o.properties&&n.push(...o.properties);return n}static hasTemplate(e,t,a){const i=this.findItem(e,t);return i?i.templateId===a?!0:i.modifications?i.modifications.some(n=>n.source==="template"&&n.name.includes(a)):!1:!1}static getAppliedTemplates(e,t){const a=[],i=this.findItem(e,t);if(!i)return a;if(i.templateId&&a.push(i.templateId),i.modifications){for(const n of i.modifications)if(n.source==="template"){const r=n.id.match(/template_([^_]+)/);r&&a.push(r[1])}}return a}static removeAllModifications(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications||i.modifications.length===0)return e;const n=i.modifications.map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static disenchant(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications)return e;const n=i.modifications.filter(o=>o.source==="enchantment"||o.source==="upgrade").map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static liftCurse(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications)return e;const n=i.modifications.filter(o=>o.source==="curse").map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static addModificationToEquipment(e,t,a,i){const n={weapons:e.weapons.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),armor:e.armor.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),items:e.items.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight},r=this.findItem(n,t);if(!r)return e;const o=this.getEquipmentData(t);if(!o)return e;r.modifications||(r.modifications=[]),r.instanceId||(r.instanceId=`${t}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const l=r.equipped,c=r.instanceId;if(l&&i&&le.unequipItem(i,t,c),r.modifications.push(a),l&&i){le.equipItem(i,o,c);for(const u of r.modifications)this.applyModificationEffects(i,o,u,c)}return n}static findItem(e,t){return e.weapons.find(i=>i.name===t)||e.armor.find(i=>i.name===t)||e.items.find(i=>i.name===t)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static applyModificationEffects(e,t,a,i){const n={...t,properties:[...t.properties||[],...a.properties],grantsFeatures:a.addsFeatures,grantsSkills:a.addsSkills,grantsSpells:a.addsSpells};le.equipItem(e,n,i)}static createModification(e,t,a,i){return{id:e,name:t,properties:a,appliedAt:new Date().toISOString(),source:i}}static createFeatureModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsFeatures:i,appliedAt:new Date().toISOString(),source:n}}static createSkillModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsSkills:i,appliedAt:new Date().toISOString(),source:n}}static createSpellModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsSpells:i,appliedAt:new Date().toISOString(),source:n}}static generateModificationId(e="mod"){return`${e}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}static getModificationSources(e,t){const a=this.findItem(e,t);if(!a||!a.modifications)return[];const i=new Set(a.modifications.map(n=>n.source));return Array.from(i)}static countModificationsBySource(e,t){const a=this.findItem(e,t);if(!a||!a.modifications)return{};const i={};for(const n of a.modifications)i[n.source]=(i[n.source]||0)+1;return i}static countModificationsForSource(e,t,a){return this.countModificationsBySource(e,t)[a]||0}static isCursed(e,t){return this.countModificationsForSource(e,t,"curse")>0}static isEnchanted(e,t){return this.getModificationSources(e,t).some(i=>i==="enchantment"||i==="upgrade")}static getItemSummary(e,t){const a=this.findItem(e,t);return a?{name:a.name,quantity:a.quantity,equipped:a.equipped,instanceId:a.instanceId,templateId:a.templateId,modificationCount:a.modifications?.length||0,isCursed:this.isCursed(e,t),isEnchanted:this.isEnchanted(e,t),sources:this.getModificationSources(e,t),effects:this.getCombinedEffects(e,t)}:null}}const Ft={plus_one_weapon:{rarity:"uncommon",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+1"]},plus_two_weapon:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+2"]},plus_three_weapon:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+3"]},flaming_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],tags:["magic","fire","flaming"]},frost_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],tags:["magic","cold","frost"]},shocking_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage",condition:{type:"on_hit",value:!0}}],tags:["magic","lightning","shocking"]},vicious_weapon_template:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"extra_damage_on_hit",value:"1d8",description:"Deals 1d8 extra damage, but wielder takes 1d8 necrotic",condition:{type:"on_hit",value:!0}}],tags:["magic","vicious","necrotic"]},plus_one_armor:{rarity:"rare",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 AC bonus",stackable:!0}],tags:["magic","enhanced","+1","armor"]},plus_two_armor:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus",stackable:!0}],tags:["magic","enhanced","+2","armor"]}};class qn{static openBox(e,t,a){if(!e.boxContents)return{success:!1,items:[],gold:0,consumeBox:!1,error:{code:"NO_BOX_CONTENTS",message:`Box "${e.name}" has no contents defined.`}};if(a!==void 0&&e.boxContents.openRequirements){const c=this.checkRequirements(e,a);if(c)return{success:!1,items:[],gold:0,consumeBox:!1,error:c}}const i=e.boxContents,n=[];let r=0;for(const c of i.drops){const u=this.selectFromPool(c.pool,t);if(u){if(u.gold!==void 0&&u.gold>0)r+=u.gold;else if(u.itemName){const d=this.getEquipmentData(u.itemName);if(d){const h=u.quantity??1;if(d.type==="box")n.push({...d});else for(let p=0;p<h;p++)n.push(d)}else console.warn(`BoxOpener: Item "${u.itemName}" not found in equipment registry. Skipping drop.`)}}}const o=i.consumeOnOpen!==!1;let l;return a!==void 0&&i.openRequirements&&(l=i.openRequirements.map(c=>({name:c.itemName,quantity:c.quantity??1}))),{success:!0,items:n,gold:r,consumeBox:o,consumedItems:l}}static selectFromPool(e,t){if(!e||e.length===0)return;const a=e.map(i=>[i,i.weight]);return t.weightedChoice(a)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static isBox(e){return e.type==="box"&&e.boxContents!==void 0}static checkRequirements(e,t){if(!e.boxContents?.openRequirements)return null;const a=e.boxContents.openRequirements;for(const i of a){const n=i.quantity??1,r=t.find(o=>o.name===i.itemName);if(!r)return{code:"MISSING_ITEM",message:`Missing required item: ${i.itemName}`,requirement:i};if(r.quantity<n)return{code:"INSUFFICIENT_QUANTITY",message:`Insufficient ${i.itemName}: have ${r.quantity}, need ${n}`,requirement:i}}return null}static canOpen(e,t){return e.boxContents?.openRequirements?this.checkRequirements(e,t)===null:!0}static getRequirementsDescription(e){if(!e.boxContents?.openRequirements||e.boxContents.openRequirements.length===0)return null;const t=e.boxContents.openRequirements,a=[];for(const i of t){const n=i.quantity??1;n===1?a.push(i.itemName):a.push(`${n} ${i.itemName}`)}return`Requires: ${a.join(", ")}`}static previewContents(e){if(!e.boxContents)return{possibleItems:[],possibleGold:{min:0,max:0},totalDrops:0};const t=new Set;let a=0,i=0;for(const r of e.boxContents.drops)for(const o of r.pool)o.itemName&&t.add(o.itemName),o.gold!==void 0&&(a=Math.min(a,o.gold),i=Math.max(i,o.gold));const n={possibleItems:Array.from(t),possibleGold:{min:a,max:i},totalDrops:e.boxContents.drops.length};return e.boxContents.openRequirements&&e.boxContents.openRequirements.length>0&&(n.openRequirements=e.boxContents.openRequirements),n}}const ze={common:0,uncommon:1,rare:2,very_rare:3,legendary:4};class Yh{static spawnFromList(e,t){const i=R.getInstance().get("equipment");let n=e;return t&&(n=t.shuffle([...e])),n.map(r=>i.find(o=>o.name===r))}static spawnByRarity(e,t,a){const r=R.getInstance().get("equipment").filter(d=>d.rarity===e&&(d.spawnWeight??1)>0);if(r.length===0)return[];const o=Math.min(t,r.length),l=[],c=a?()=>a.random():Math.random,u=[...r];for(let d=u.length-1;d>0;d--){const h=Math.floor(c()*(d+1));[u[d],u[h]]=[u[h],u[d]]}for(let d=0;d<o;d++)l.push(u[d]);return l}static spawnByTags(e,t,a,i){const o=R.getInstance().get("equipment").filter(d=>{if(i?.excludeZeroWeight&&(d.spawnWeight??1)<=0||i?.includeTypes&&i.includeTypes.length>0&&!i.includeTypes.includes(d.type))return!1;const h=ze[d.rarity];return i?.minRarity&&h<ze[i.minRarity]||i?.maxRarity&&h>ze[i.maxRarity]?!1:d.tags&&e.some(p=>d.tags.includes(p))});if(o.length===0)return[];const l=Math.min(t,o.length),c=[],u=a?()=>a.random():Math.random;for(let d=0;d<l;d++){const h=o.map(f=>[f,f.spawnWeight??1]);let p=h.reduce((f,[,v])=>f+v,0),g=u()*p,m;for(const[f,v]of h)if(g-=v,g<=0){m=f;break}if(m){c.push(m);const f=o.indexOf(m);f>-1&&o.splice(f,1)}}return c}static spawnRandom(e,t,a){const r=R.getInstance().get("equipment").filter(l=>{const c=l.spawnWeight??1;if(a?.excludeZeroWeight&&c<=0||a?.includeTypes&&a.includeTypes.length>0&&!a.includeTypes.includes(l.type))return!1;const u=ze[l.rarity];return a?.minRarity&&u<ze[a.minRarity]||a?.maxRarity&&u>ze[a.maxRarity]?!1:c>0});if(r.length===0)return[];const o=[];for(let l=0;l<e&&r.length>0;l++){const c=r.map(m=>[m,m.spawnWeight??1]),u=c.reduce((m,[,f])=>m+f,0);let d=t.random()*u,h=0;for(let m=0;m<c.length;m++)if(d-=c[m][1],d<=0){h=m;break}const p=c[h][0];o.push(p);const g=r.indexOf(p);g>-1&&r.splice(g,1)}return o}static spawnFromTemplate(e,t){const a=R.getInstance();let i;const n=a.get("equipment.templates");if(Array.isArray(n)?i=n.find(o=>o.id===e||o.name===e):n&&typeof n=="object"&&(i=n[e]),!i&&e in Ft&&(i=Ft[e]),!i)return null;let r;return t?r=a.get("equipment").find(l=>l.name===t):r=this.getDefaultItemForTemplate(i),r?{...r,...i,name:`${r.name} (${e.replace(/_/g," ")})`,templateId:e,properties:[...r.properties||[],...i.properties||[]],tags:[...r.tags||[],...i.tags||[]]}:null}static spawnTreasureHoard(e,t){const a=[];let i=0;const n=this.getTreasureItemCount(e,t);for(const[r,o]of Object.entries(n))for(let l=0;l<o;l++){const c=r,u=this.spawnByRarity(c,1,t);u.length>0&&(a.push(u[0]),i+=this.estimateItemValue(u[0]))}if(a.length===0){const r=this.spawnByRarity("common",1,t);r.length>0&&(a.push(r[0]),i+=this.estimateItemValue(r[0]))}return{items:a,totalValue:i,cr:e}}static addToCharacter(e,t,a=!1){e.equipment||(e.equipment={weapons:[],armor:[],items:[],totalWeight:0,equippedWeight:0});for(const i of t){const n={name:i.name,quantity:1,equipped:a,instanceId:`${i.name}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`};switch(i.type){case"weapon":e.equipment.weapons.push(n);break;case"armor":e.equipment.armor.push(n);break;case"item":case"box":e.equipment.items.push(n);break}e.equipment.totalWeight+=i.weight,a&&(e.equipment.equippedWeight+=i.weight)}return e}static openBoxForCharacter(e,t,a){if(!e.equipment?.items||e.equipment.items.findIndex(d=>d.name===t)===-1)return null;const o=R.getInstance().get("equipment").find(d=>d.name===t);if(!o||o.type!=="box")return null;const l=e.equipment.items,c=qn.openBox(o,a,l);if(!c.success)return{character:e,result:c};if(c.consumedItems&&c.consumedItems.length>0)for(const d of c.consumedItems)e=this.consumeItemFromCharacter(e,d.name,d.quantity).character;if(c.consumeBox&&e.equipment?.items){const d=e.equipment.items.findIndex(h=>h.name===t);d!==-1&&(e.equipment.items.splice(d,1),e.equipment.totalWeight-=o.weight)}const u=c.items;return e=this.addToCharacter(e,u,!1),{character:e,result:c}}static consumeItemFromCharacter(e,t,a){if(!e.equipment?.items)return{success:!1,character:e};const i=e.equipment.items.findIndex(u=>u.name===t);if(i===-1)return{success:!1,character:e};const n=e.equipment.items[i];if(n.quantity<a)return{success:!1,character:e};const c=R.getInstance().get("equipment").find(u=>u.name===t)?.weight??0;return n.quantity-=a,e.equipment.totalWeight-=c*a,n.quantity<=0&&e.equipment.items.splice(i,1),{success:!0,character:e}}static getDefaultItemForTemplate(e){const a=R.getInstance().get("equipment");let i="item";return e.tags&&(e.tags.includes("weapon")?i="weapon":e.tags.includes("armor")&&(i="armor")),a.find(n=>n.type===i)}static getTreasureItemCount(e,t){const a={},i=()=>t.random();return e<5?(a.common=Math.floor(i()*3)+1,a.uncommon=i()>.7?1:0):e<11?(a.common=Math.floor(i()*2)+1,a.uncommon=Math.floor(i()*2)+1,a.rare=i()>.6?1:0):e<17?(a.uncommon=Math.floor(i()*2)+1,a.rare=Math.floor(i()*2)+1,a.very_rare=i()>.7?1:0):(a.rare=Math.floor(i()*2)+1,a.very_rare=Math.floor(i()*2)+1,a.legendary=i()>.5?1:0),a}static estimateItemValue(e){let a={common:50,uncommon:400,rare:4e3,very_rare:4e4,legendary:2e5}[e.rarity]||50;return e.type==="weapon"?a*=1.2:e.type==="armor"&&(a*=1.5),e.weight>20&&(a*=1.1),Math.round(a)}}const aa="geolocation_cache";class ls{constructor(e,t){if(this.cache=null,this.cacheTTL=300*1e3,this.cacheStats={hits:0,misses:0},this.logger=z.Logger.for("GeolocationProvider"),typeof e=="number"||e===void 0)this.cacheTTL=(e??5)*60*1e3,this.useLocalStorage=(t??!0)&&this.isLocalStorageAvailable();else{const a=e;this.cacheTTL=a.cacheTTL??300*1e3,this.useLocalStorage=(a.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const e="__localStorage_test__";return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return!1}}loadFromLocalStorage(){try{const e=localStorage.getItem(aa);if(e){const t=JSON.parse(e),a=Date.now(),i=Object.values(t)[0];i&&a-i.timestamp<this.cacheTTL&&(this.cache=i)}}catch(e){this.logger.warn("Failed to load geolocation cache from localStorage",{error:e})}}saveToLocalStorage(){if(!(!this.useLocalStorage||!this.cache))try{const e={position:this.cache};localStorage.setItem(aa,JSON.stringify(e))}catch(e){this.logger.warn("Failed to save geolocation cache to localStorage",{error:e})}}isCacheEntryValid(e){return e?Date.now()-e.timestamp<this.cacheTTL:!1}getCacheAge(){return this.cache?Date.now()-this.cache.timestamp:null}async getCurrentPosition(e=!1){return!e&&this.isCacheEntryValid(this.cache)?(this.cacheStats.hits++,this.cache.data):(this.cacheStats.misses++,typeof navigator>"u"||!navigator.geolocation?null:new Promise(t=>{navigator.geolocation.getCurrentPosition(a=>{const i={latitude:a.coords.latitude,longitude:a.coords.longitude,altitude:a.coords.altitude,accuracy:a.coords.accuracy,heading:a.coords.heading,speed:a.coords.speed,timestamp:a.timestamp};this.cache={data:i,timestamp:Date.now()},this.saveToLocalStorage(),t(i)},a=>{this.logger.warn("Geolocation error",{error:a.message}),t(null)},{enableHighAccuracy:!0,timeout:5e3,maximumAge:0})}))}invalidateCache(){if(this.cache=null,this.useLocalStorage)try{localStorage.removeItem(aa)}catch(e){this.logger.warn("Failed to clear geolocation cache from localStorage",{error:e})}}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}isCacheExpired(){return!this.isCacheEntryValid(this.cache)}getCachedPosition(){return this.cache?.data??null}getBiome(e,t,a=null){const i=Math.abs(e),n=this.normalizeLongitude(t),r=this.isCoastal(e,t),o=r?"_coastal":"";if(a!==null&&!isNaN(a)){const l=this.getElevationBiome(a,o);if(l)return l}if(i>66.5)return`tundra${o}`;if(this.isInSwampRegion(e,n))return`swamp${o}`;if(i>=15&&i<=45&&this.isInDesertRegion(e,n))return r?"coastal_desert":"desert";if(i<=23.5){if(this.isInJungleRegion(e,n))return`jungle${o}`;if(this.isInSavannaRegion(e,n))return`savanna${o}`;if(e>0){if(n>=290&&n<=310)return`forest${o}`;if(n>=10&&n<=30)return`forest${o}`;if(n>=70&&n<=120)return`forest${o}`}else{if(n>=10&&n<=30)return`forest${o}`;if(n>=100&&n<=140)return`forest${o}`}return`forest${o}`}if(e>0&&i>=30&&i<=50&&(n>=235&&n<=290||n>=0&&n<=40||n>=110&&n<=145))return r?"coastal_urban":"urban";if(i>23.5&&i<=66.5)if(e>0){if(this.isInTaigaRegion(e,n))return`taiga${o}`;if(n>=235&&n<=290)return i>=45?`forest${o}`:`plains${o}`;if(n>=0&&n<=40)return`forest${o}`;if(n>40&&n<=180)return i>=50?`mountain${o}`:`plains${o}`}else{if(n>=280&&n<=320)return`plains${o}`;if(n>=15&&n<=40)return`plains${o}`;if(n>=110&&n<=180)return`plains${o}`}return`plains${o}`}getElevationBiome(e,t){return e>3500?`mountain${t}`:e>1500?`mountain${t}`:e<0?`valley${t}`:null}normalizeLongitude(e){let t=e%360;return t<0&&(t+=360),t}isInJungleRegion(e,t){return Math.abs(e)>15?!1:e>=-15&&e<=5&&t>=290&&t<=310||e>=-5&&e<=5&&t>=10&&t<=30||e>=-10&&e<=10&&t>=95&&t<=140}isInSwampRegion(e,t){return e>=25&&e<=26&&t>=279&&t<=281||e>=-20&&e<=-18&&t>=22&&t<=24||e>=21&&e<=22&&t>=89&&t<=90||e>=-20&&e<=-15&&t>=300&&t<=305}isInTaigaRegion(e,t){return e<50||e>70?!1:e>=50&&e<=70&&t>=230&&t<=300||e>=60&&e<=70&&t>=5&&t<=30||e>=55&&e<=70&&t>=30&&t<=180}isInSavannaRegion(e,t){return Math.abs(e)>20?!1:e>=-5&&e<=15&&t>=30&&t<=40||e>=-20&&e<=-15&&t>=15&&t<=35||e>=-25&&e<=-5&&t>=300&&t<=315||e>=-20&&e<=-10&&t>=130&&t<=135}isInDesertRegion(e,t){return e>15&&e<30&&(t>=345||t<=40)||e>15&&e<30&&t>=35&&t<=55||e>28&&e<35&&t>=35&&t<=40||e>25&&e<35&&t>=50&&t<=65||e>23&&e<30&&t>=68&&t<=75||e>35&&e<45&&t>=100&&t<=115||e<-20&&e>-30&&t>=115&&t<=145||e<-20&&e>-25&&t>=290&&t<=292||e>25&&e<35&&t>=245&&t<=250||e<-20&&e>-30&&t>=20&&t<=30}isCoastal(e,t){const a=Math.abs(e),i=this.normalizeLongitude(t);return!!(this.isInSmallIslandRegion(e,i)||this.isInNarrowLandmass(e,i)||a>60||e>30&&e<45&&i>=0&&i<=25||e>30&&e<40&&i>=25&&i<=35||e>15&&e<30&&i>=35&&i<=43||e>24&&e<30&&i>=48&&i<=55||e>41&&e<47&&i>=27&&i<=42||e>36&&e<47&&i>=46&&i<=55||e>53&&e<66&&i>=15&&i<=30||e>50&&e<60&&i>=0&&i<=10||e>15&&e<25&&i>=65&&i<=75||e>15&&e<23&&i>=80&&i<=90||e>35&&e<43&&i>=130&&i<=142||e>10&&e<25&&i>=105&&i<=122||e>20&&e<30&&i>=265&&i<=280||e>15&&e<25&&i>=275&&i<=300)}isInSmallIslandRegion(e,t){return e>=50&&e<=60&&t>=358&&t<=360||e>=50&&e<=60&&t>=0&&t<=10||e>=30&&e<=46&&t>=128&&t<=146||e>=4&&e<=22&&t>=116&&t<=127||e>=-10&&e<=6&&t>=94&&t<=142||e>=-47&&e<=-34&&t>=165&&t<=179||e>=-26&&e<=-12&&t>=43&&t<=51||e>=63&&e<=67&&t>=338&&t<=344||e>=10&&e<=23&&t>=275&&t<=300||e>=5&&e<=10&&t>=79&&t<=82||e>=18&&e<=23&&t>=204&&t<=206||e>=-18&&e<=-12&&t>=176&&t<=181}isInNarrowLandmass(e,t){return e>=7&&e<=18&&t>=275&&t<=290||e>=33&&e<=43&&t>=124&&t<=132||e>=36&&e<=47&&t>=8&&t<=19||e>=36&&e<=44&&t>=358&&t<=360||e>=36&&e<=44&&t>=0&&t<=10||e>=55&&e<=71&&t>=4&&t<=32||e>=24&&e<=31&&t>=268&&t<=272||e>=55&&e<=62&&t>=210&&t<=220||e>=50&&e<=62&&t>=156&&t<=163}}class Zh{constructor(){this.logger=z.Logger.for("MotionDetector"),this.isListening=!1,this.lastMotion=null,this.motionCallback=null,this.lastProcessedTime=0,this.minSampleIntervalMs=83,this.deltaBuffer=[],this.bufferSize=24,this.confirmedActivity="unknown",this.candidateActivity=null,this.candidateStartTime=0,this.confirmationMs=1500,this.handleMotion=e=>{if(!this.isListening||!this.motionCallback)return;const t=Date.now(),a={acceleration:{x:e.acceleration?.x??null,y:e.acceleration?.y??null,z:e.acceleration?.z??null},accelerationIncludingGravity:{x:e.accelerationIncludingGravity?.x??0,y:e.accelerationIncludingGravity?.y??0,z:e.accelerationIncludingGravity?.z??0},rotationRate:{alpha:e.rotationRate?.alpha??null,beta:e.rotationRate?.beta??null,gamma:e.rotationRate?.gamma??null},interval:e.interval,timestamp:t};if(this.lastMotion=a,t-this.lastProcessedTime<this.minSampleIntervalMs)return;this.lastProcessedTime=t;const i=a.accelerationIncludingGravity;if(i.x!=null&&i.y!=null&&i.z!=null&&!(i.x===0&&i.y===0&&i.z===0)){const n=Math.sqrt(i.x**2+i.y**2+i.z**2),r=Math.abs(n-9.8);this.updateSmoothing(r)}this.logger.debug("Motion event received",{acceleration:a.accelerationIncludingGravity,rotation:a.rotationRate,activity:this.detectActivity(a)}),this.motionCallback(a)}}startMonitoring(e){if(typeof window>"u"||!("DeviceMotionEvent"in window)){this.logger.warn("DeviceMotionEvent not supported");return}this.motionCallback=e,this.isListening=!0,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null,window.addEventListener("devicemotion",this.handleMotion)}stopMonitoring(){typeof window<"u"&&window.removeEventListener("devicemotion",this.handleMotion),this.isListening=!1,this.motionCallback=null,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null}getLastMotion(){return this.lastMotion}detectActivity(e){const t=e.accelerationIncludingGravity;if(t.x==null||t.y==null||t.z==null||t.x===0&&t.y===0&&t.z===0)return"unknown";if(this.deltaBuffer.length>=10)return this.confirmedActivity;const a=Math.sqrt(t.x**2+t.y**2+t.z**2),i=Math.abs(a-9.8);return i<.5?"stationary":i<2?"walking":i<5?"running":"driving"}updateSmoothing(e){this.deltaBuffer.push(e),this.deltaBuffer.length>this.bufferSize&&this.deltaBuffer.shift();const t=this.deltaBuffer.reduce((i,n)=>i+n,0)/this.deltaBuffer.length;let a;if(t<.5?a="stationary":t<2?a="walking":t<5?a="running":a="driving",a===this.confirmedActivity){this.candidateActivity=null;return}if(a!==this.candidateActivity){this.candidateActivity=a,this.candidateStartTime=Date.now();return}Date.now()-this.candidateStartTime>=this.confirmationMs&&(this.confirmedActivity=a,this.candidateActivity=null)}}const Un=W({id:A(),main:T(),description:T(),icon:T().optional()}),Gn=W({temp:A(),feels_like:A().optional(),temp_min:A().optional(),temp_max:A().optional(),pressure:A(),humidity:A().nonnegative(),sea_level:A().optional(),grnd_level:A().optional(),temp_kf:A().optional()}),jn=W({speed:A().nonnegative(),deg:A().min(0).max(360).optional(),gust:A().optional()}),Vn=W({all:A().min(0).max(100)}),Xh=W({type:A().optional(),id:A().optional(),country:T().optional(),sunrise:A().nonnegative(),sunset:A().nonnegative(),pod:T().optional()}),Yn=W({"1h":A().optional(),"3h":A().optional()}),Zn=W({"1h":A().optional(),"3h":A().optional()}),Xn=W({lon:A().min(-180).max(180),lat:A().min(-90).max(90)}),Kh=W({coord:Xn.optional(),weather:ne(Un).min(1),base:T().optional(),main:Gn,visibility:A().nonnegative().optional(),wind:jn.optional(),rain:Yn.optional(),snow:Zn.optional(),clouds:Vn.optional(),dt:A().nonnegative(),sys:Xh,timezone:A().optional(),id:A().optional(),name:T().optional(),cod:A().optional()}),Jh=W({dt:A().nonnegative(),main:Gn,weather:ne(Un).min(1),clouds:Vn.optional(),wind:jn.optional(),visibility:A().nonnegative().optional(),pop:A().min(0).max(1).optional(),rain:Yn.optional(),snow:Zn.optional(),sys:W({pod:T().optional()}).optional(),dt_txt:T().optional()}),Qh=W({cod:Ha([T(),A()]),message:A().optional(),cnt:A().nonnegative().optional(),list:ne(Jh).min(1),city:W({id:A(),name:T(),coord:Xn,country:T().optional(),population:A().optional(),timezone:A().optional(),sunrise:A().optional(),sunset:A().optional()}).optional()}),cs="weather_api_cache";class ep{constructor(e,t,a){if(this.baseUrl="https://api.openweathermap.org/data/2.5/weather",this.forecastUrl="https://api.openweathermap.org/data/2.5/forecast",this.cache=new Map,this.forecastCache=new Map,this.cacheTTL=720*1e3,this.forecastCacheTTL=3600*1e3,this.cacheStats={hits:0,misses:0},this.logger=z.Logger.for("WeatherAPIClient"),this.lastKnownLocation=null,this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[],this.maxRecentSamples=100,typeof e=="string"||e===void 0)this.apiKey=e??"",this.cacheTTL=(t??12)*60*1e3,this.useLocalStorage=(a??!0)&&this.isLocalStorageAvailable();else{const i=e;this.apiKey=i.apiKey??"",this.cacheTTL=i.cacheTTL??720*1e3,this.useLocalStorage=(i.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const e="__localStorage_test__";return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return!1}}loadFromLocalStorage(){try{const e=localStorage.getItem(cs);if(e){const t=JSON.parse(e),a=Date.now();for(const[i,n]of Object.entries(t))a-n.timestamp<this.cacheTTL&&this.cache.set(i,n)}}catch(e){this.logger.warn("Failed to load weather cache from localStorage",{error:e})}}saveToLocalStorage(){if(this.useLocalStorage)try{const e={};for(const[t,a]of this.cache.entries())e[t]=a;localStorage.setItem(cs,JSON.stringify(e))}catch(e){this.logger.warn("Failed to save weather cache to localStorage",{error:e})}}startTiming(){const e=performance.now();return t=>{performance.now()-e}}recordWeatherApiCall(e,t){const a=Date.now();t?(this.weatherApiMetrics.successCount++,this.weatherApiMetrics.totalTime+=e,this.weatherApiMetrics.minTime=Math.min(this.weatherApiMetrics.minTime,e),this.weatherApiMetrics.maxTime=Math.max(this.weatherApiMetrics.maxTime,e),this.recentWeatherApiTimes.push(e),this.recentWeatherApiTimes.length>this.maxRecentSamples&&this.recentWeatherApiTimes.shift()):this.weatherApiMetrics.errorCount++,this.weatherApiMetrics.lastCallTimestamp=a}recordForecastApiCall(e,t){const a=Date.now();t?(this.forecastApiMetrics.successCount++,this.forecastApiMetrics.totalTime+=e,this.forecastApiMetrics.minTime=Math.min(this.forecastApiMetrics.minTime,e),this.forecastApiMetrics.maxTime=Math.max(this.forecastApiMetrics.maxTime,e),this.recentForecastApiTimes.push(e),this.recentForecastApiTimes.length>this.maxRecentSamples&&this.recentForecastApiTimes.shift()):this.forecastApiMetrics.errorCount++,this.forecastApiMetrics.lastCallTimestamp=a}calculatePercentile(e,t){if(e.length===0)return 0;const a=[...e].sort((n,r)=>n-r),i=Math.ceil(t/100*a.length)-1;return a[Math.max(0,i)]}getPerformanceStatistics(e,t){const a=e.successCount+e.errorCount,i=e.successCount>0?e.totalTime/e.successCount:0,n=a>0?e.successCount/a*100:0;return{average:Math.round(i),min:e.minTime===1/0?0:Math.round(e.minTime),max:Math.round(e.maxTime),totalCalls:a,successRate:Math.round(n*10)/10}}getWeatherApiMetrics(){return{...this.weatherApiMetrics}}getWeatherApiStatistics(){return{...this.getPerformanceStatistics(this.weatherApiMetrics,this.recentWeatherApiTimes),p95:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,99))}}getForecastApiMetrics(){return{...this.forecastApiMetrics}}getForecastApiStatistics(){return{...this.getPerformanceStatistics(this.forecastApiMetrics,this.recentForecastApiTimes),p95:Math.round(this.calculatePercentile(this.recentForecastApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentForecastApiTimes,99))}}resetPerformanceMetrics(){this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[]}getCacheKey(e,t){const a=Math.round(e*1e4)/1e4,i=Math.round(t*1e4)/1e4;return`${a},${i}`}isCacheEntryValid(e){return Date.now()-e.timestamp<this.cacheTTL}calculateMoonPhase(e){const t=new Date("2024-01-11T11:57:00Z"),a=29.530588853*24*60*60*1e3,n=(e.getTime()-t.getTime())/a;return Math.abs(n%1)}getSolarInfo(e,t,a){const i=a??new Date;return{...this.calculateSolarInfo(e,t,i),fromApi:!1,timestamp:Date.now()}}calculateSolarInfo(e,t,a){const i=this.calculateSunriseSunset(e,t,a,!0),n=this.calculateSunriseSunset(e,t,a,!1),r=this.calculateSolarNoon(e,t,a),o=this.calculateTwilight(e,t,a,"civil",!0),l=this.calculateTwilight(e,t,a,"civil",!1),{altitude:c,azimuth:u}=this.calculateSunPosition(e,t,a),d=!isNaN(n.getTime())&&!isNaN(i.getTime())?(n.getTime()-i.getTime())/(1e3*60*60):0,h=this.determineDayStage(a,i,n,o,l);return{currentTime:a,stage:h,sunrise:i,sunset:n,solarNoon:r,civilDawn:o,civilDusk:l,sunAltitude:c,sunAzimuth:u,dayLengthHours:d}}toRadians(e){return e*(Math.PI/180)}toDegrees(e){return e*(180/Math.PI)}calculateJulianDate(e){const t=e.getUTCFullYear(),a=e.getUTCMonth()+1,i=e.getUTCDate(),n=e.getUTCHours()+e.getUTCMinutes()/60+e.getUTCSeconds()/3600;let r=Math.floor((14-a)/12),o=t+4800-r,l=a+12*r-3;return i+Math.floor((153*l+2)/5)+365*o+Math.floor(o/4)-Math.floor(o/100)+Math.floor(o/400)-32045+(n-12)/24}calculateJulianCentury(e){return(e-2451545)/36525}calculateSolarDeclination(e){const t=(280.46646+e*(36000.76983+e*3032e-7))%360,a=357.52911+e*(35999.05029-1537e-7*e),i=Math.sin(this.toRadians(a))*(1.914602-e*(.004817+14e-6*e))+Math.sin(this.toRadians(2*a))*(.019993-101e-6*e)+Math.sin(this.toRadians(3*a))*289e-6,n=t+i,o=23+(26+(21.448-e*(46.815+e*(59e-5-e*.001813)))/60)/60+.00256*Math.cos(this.toRadians(125.04-1934.136*e)),l=n-.00569-.00478*Math.sin(this.toRadians(125.04-1934.136*e));return this.toDegrees(Math.asin(Math.sin(this.toRadians(o))*Math.sin(this.toRadians(l))))}calculateEquationOfTime(e){const t=(280.46646+e*(36000.76983+e*3032e-7))%360,a=357.52911+e*(35999.05029-1537e-7*e),i=.016708634-e*(42037e-9+1267e-10*e);return Math.tan(this.toRadians(23.44/2))**2,4*this.toDegrees(Math.atan2(Math.sin(this.toRadians(2*t))*Math.cos(this.toRadians(a))*2*i-Math.sin(this.toRadians(a))*2*i+Math.sin(this.toRadians(t))*Math.cos(this.toRadians(a))*2*i*Math.cos(this.toRadians(t)),1-Math.sin(this.toRadians(t))*Math.sin(this.toRadians(t))*i*i*Math.cos(this.toRadians(a))*Math.cos(this.toRadians(a))))}calculateHourAngle(e,t,a=90.833){const i=this.toRadians(e),n=this.toRadians(t),r=this.toRadians(a),o=(Math.cos(r)-Math.sin(i)*Math.sin(n))/(Math.cos(i)*Math.cos(n));return o>1?NaN:o<-1?NaN:this.toDegrees(Math.acos(o))}calculateSunriseSunset(e,t,a,i){const n=new Date(a);n.setUTCHours(12,0,0,0);const r=this.calculateJulianDate(n),o=this.calculateJulianCentury(r),l=this.calculateSolarDeclination(o),c=this.calculateHourAngle(e,l);if(isNaN(c))return new Date(NaN);const u=this.calculateEquationOfTime(o),d=(i?-c:c)/15,p=12-t/15-u/60+d,g=Math.floor(p),m=Math.floor((p-g)*60),f=Math.floor(((p-g)*60-m)*60),v=new Date(a);return v.setUTCHours(g,m,f,0),v}calculateSolarNoon(e,t,a){const i=new Date(a);i.setUTCHours(12,0,0,0);const n=this.calculateJulianDate(i),r=this.calculateJulianCentury(n),o=this.calculateEquationOfTime(r),l=12-t/15-o/60,c=Math.floor(l),u=Math.floor((l-c)*60),d=Math.floor(((l-c)*60-u)*60),h=new Date(a);return h.setUTCHours(c,u,d,0),h}calculateTwilight(e,t,a,i,n){const o={civil:96,nautical:102,astronomical:108}[i],l=new Date(a);l.setUTCHours(12,0,0,0);const c=this.calculateJulianDate(l),u=this.calculateJulianCentury(c),d=this.calculateSolarDeclination(u),h=this.calculateHourAngle(e,d,o);if(isNaN(h))return new Date(NaN);const p=this.calculateEquationOfTime(u),g=(n?-h:h)/15,f=12-t/15-p/60+g,v=Math.floor(f),w=Math.floor((f-v)*60),E=Math.floor(((f-v)*60-w)*60),I=new Date(a);return I.setUTCHours(v,w,E,0),I}calculateSunPosition(e,t,a){const i=this.calculateJulianDate(a),n=this.calculateJulianCentury(i),r=this.calculateSolarDeclination(n),o=this.calculateEquationOfTime(n),u=(a.getUTCHours()+a.getUTCMinutes()/60+a.getUTCSeconds()/3600+t/15+o/60-12)*15,d=this.toRadians(e),h=this.toRadians(r),p=this.toRadians(u),g=Math.sin(d)*Math.sin(h)+Math.cos(d)*Math.cos(h)*Math.cos(p),m=this.toDegrees(Math.asin(g)),f=(Math.sin(h)-Math.sin(d)*g)/(Math.cos(d)*Math.cos(this.toRadians(m)));let v=this.toDegrees(Math.acos(Math.max(-1,Math.min(1,f))));return u>0&&(v=360-v),{altitude:m,azimuth:v}}determineDayStage(e,t,a,i,n){const r=e.getTime();if(isNaN(t.getTime())||isNaN(a.getTime())){const d=e.getUTCMonth();return d>=4&&d<=7?"day":"night"}const o=i&&!isNaN(i.getTime())?i.getTime():t.getTime(),l=n&&!isNaN(n.getTime())?n.getTime():a.getTime(),c=t.getTime(),u=a.getTime();return r<o||r>l?"night":r>=o&&r<c?"dawn":r>u&&r<=l?"dusk":"day"}async getWeather(e,t){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;this.lastKnownLocation={latitude:e,longitude:t};const a=this.getCacheKey(e,t),i=this.cache.get(a);if(i&&this.isCacheEntryValid(i))return this.cacheStats.hits++,i.data;this.cacheStats.misses++;const n=performance.now();let r=!1;try{const o=`${this.baseUrl}?lat=${e}&lon=${t}&appid=${this.apiKey}&units=metric`,l=await fetch(o);if(!l.ok)throw new Error(`Weather API error: ${l.statusText}`);const c=await l.json(),u=Kh.safeParse(c);if(!u.success)return this.logger.error("Weather API response validation failed",{errors:u.error.issues,response:c}),null;const d=u.data,h=Date.now()/1e3,p=h<d.sys.sunrise||h>d.sys.sunset,g={temperature:d.main.temp,humidity:d.main.humidity,pressure:d.main.pressure,weatherType:d.weather[0]?.main||"Clear",windSpeed:d.wind?.speed??0,windDirection:d.wind?.deg??0,isNight:p,moonPhase:this.calculateMoonPhase(new Date),timestamp:Date.now()};return this.cache.set(a,{data:g,timestamp:Date.now()}),this.saveToLocalStorage(),r=!0,g}catch(o){return this.logger.error("Failed to fetch weather",{error:o}),null}finally{const o=performance.now()-n;this.recordWeatherApiCall(o,r)}}invalidateCache(){this.cache.clear(),this.saveToLocalStorage()}invalidateLocation(e,t){const a=this.getCacheKey(e,t);this.cache.delete(a),this.saveToLocalStorage()}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}clearExpiredEntries(){let e=0;for(const[t,a]of this.cache.entries())this.isCacheEntryValid(a)||(this.cache.delete(t),e++);return e>0&&this.saveToLocalStorage(),e}getCacheSize(){return this.cache.size}getLastKnownLocation(){return this.lastKnownLocation?{...this.lastKnownLocation}:null}isForecastCacheEntryValid(e){return Date.now()-e.timestamp<this.forecastCacheTTL}getForecastCacheKey(e,t,a){const i=Math.round(e*1e4)/1e4,n=Math.round(t*1e4)/1e4;return`forecast_${i},${n}_${a}h`}async getForecast(e,t,a=24){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;const i=Math.min(a,120),n=this.getForecastCacheKey(e,t,i),r=this.forecastCache.get(n);if(r&&this.isForecastCacheEntryValid(r))return r.data.slice(0,Math.ceil(i/3));const o=performance.now();let l=!1;try{const c=Math.ceil(i/3),u=`${this.forecastUrl}?lat=${e}&lon=${t}&appid=${this.apiKey}&units=metric&cnt=${c}`,d=await fetch(u);if(!d.ok)throw new Error(`Weather Forecast API error: ${d.statusText}`);const h=await d.json(),p=Qh.safeParse(h);if(!p.success)return this.logger.error("Weather Forecast API response validation failed",{errors:p.error.issues,response:h}),null;const m=p.data.list.map(f=>({temperature:f.main.temp,humidity:f.main.humidity,pressure:f.main.pressure,weatherType:f.weather[0]?.main||"Clear",windSpeed:f.wind?.speed??0,windDirection:f.wind?.deg??0,timestamp:Date.now(),forecastTime:new Date(f.dt*1e3),probabilityOfPrecipitation:f.pop??0}));return this.forecastCache.set(n,{data:m,timestamp:Date.now()}),l=!0,m}catch(c){return this.logger.error("Failed to fetch weather forecast",{error:c}),null}finally{const c=performance.now()-o;this.recordForecastApiCall(c,l)}}async getUpcomingWeather(e,t,a=12){const i=await this.getForecast(e,t,a);if(!i||i.length===0)return null;let n=!1,r=!1,o=0,l=0;const c=[];for(const p of i)c.push(p.weatherType),(p.weatherType.toLowerCase().includes("rain")||p.weatherType.toLowerCase().includes("drizzle"))&&(n=!0,o=Math.max(o,p.probabilityOfPrecipitation)),p.weatherType.toLowerCase().includes("snow")&&(r=!0,l=Math.max(l,p.probabilityOfPrecipitation));const u={thunderstorm:5,snow:4,rain:3,drizzle:2,clouds:1,clear:0,mist:1,fog:1,haze:1,smoke:1,dust:1,sand:1,ash:1,squall:2,tornado:5};let d="Clear",h=-1;for(const p of new Set(c)){const g=u[p.toLowerCase()]??0;g>h&&(h=g,d=p)}return{willRain:n,willSnow:r,rainProbability:o,snowProbability:l,worstWeatherType:d}}detectSevereWeather(e){const t=e.weatherType.toLowerCase(),a=e.windSpeed*3.6;if(t.includes("snow")||t.includes("blizzard")){const i=t.includes("blizzard")||t.includes("heavy")||e.windSpeed>8,n=a>25;if(t.includes("blizzard")||i&&n)return{type:"Blizzard",xpBonus:.5,severity:a>50?"extreme":"high",message:"⚠️ Blizzard conditions detected! Stay safe and warm.",detectedAt:Date.now()}}if(a>118){const n=(this.lastKnownLocation?this.isTropicalRegion(this.lastKnownLocation.latitude):!1)?"Hurricane":"Typhoon";return{type:n,xpBonus:.75,severity:a>177?"extreme":"high",message:`🌀 ${n} conditions detected! Please seek shelter.`,detectedAt:Date.now()}}return t.includes("tornado")?{type:"Tornado",xpBonus:1,severity:"extreme",message:"🌪️ TORNADO WARNING! Take immediate shelter!",detectedAt:Date.now()}:t.includes("thunderstorm")&&a>60?{type:"Tornado",xpBonus:.5,severity:"high",message:"⛈️ Extreme thunderstorm with high winds! Exercise caution.",detectedAt:Date.now()}:null}isTropicalRegion(e){return Math.abs(e)<23.5}getSafetyWarning(e){switch(e.type){case"Blizzard":return e.severity==="extreme"?"🚨 EXTREME BLIZZARD: Stay indoors, avoid travel. Keep emergency supplies ready.":"⚠️ Blizzard: Dress warmly, avoid unnecessary travel. Check on neighbors.";case"Hurricane":case"Typhoon":return e.severity==="extreme"?"🚨 EXTREME CYCLONE: Seek shelter immediately! Follow evacuation orders.":"⚠️ Hurricane/Typhoon: Secure property, prepare emergency kit, follow local alerts.";case"Tornado":return"🚨 TORNADO: Take shelter in basement or interior room immediately! Stay away from windows.";default:return"⚠️ Severe weather detected. Stay informed and stay safe."}}invalidateForecastCache(){this.forecastCache.clear()}invalidateForecastLocation(e,t){for(const[a]of this.forecastCache.entries()){const i=this.getCacheKey(e,t);a.includes(i)&&this.forecastCache.delete(a)}}clearExpiredForecastEntries(){let e=0;for(const[t,a]of this.forecastCache.entries())this.isForecastCacheEntryValid(a)||(this.forecastCache.delete(t),e++);return e}}const S={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m"},ai={useColors:!0,compact:!1,showTimestamp:!0,maxFailures:5};function ii(s){return s.useColors===!1?!1:typeof process<"u"&&process.stdout?.isTTY===!0}function k(s,e,t){return t?`${e}${s}${S.reset}`:s}function tp(s,e){switch(s){case"healthy":return k("●",S.green,e);case"degraded":return k("●",S.yellow,e);case"failed":return k("●",S.red,e);default:return k("○",S.dim,e)}}function ap(s,e){return s?k("✓ Granted",S.green,e):k("✗ Denied",S.red,e)}function ip(s,e){return s?k("Available",S.green,e):k("Unavailable",S.red,e)}function si(s){const e=new Date(s),t=e.getHours().toString().padStart(2,"0"),a=e.getMinutes().toString().padStart(2,"0"),i=e.getSeconds().toString().padStart(2,"0");return`${t}:${a}:${i}`}function rt(s){return s<1e3?`${s}ms`:s<6e4?`${(s/1e3).toFixed(1)}s`:s<36e5?`${Math.floor(s/6e4)}m ${Math.floor(s%6e4/1e3)}s`:`${Math.floor(s/36e5)}h ${Math.floor(s%36e5/6e4)}m`}function us(s,e){const t=s+e;return t===0?"N/A":`${(s/t*100).toFixed(1)}%`}function Ht(s,e){const t=[],a=s.totalCalls>0;if(t.push(` Calls: ${s.totalCalls}`),t.push(` Success: ${a?`${s.successRate.toFixed(1)}%`:"N/A"}`),a){const i=s.average<500?S.green:s.average<1500?S.yellow:S.red;t.push(` Avg Time: ${k(`${s.average}ms`,i,e)}`),t.push(` Min/Avg/Max: ${s.min}/${s.average}/${s.max}ms`),t.push(` P95/P99: ${s.p95}/${s.p99}ms`)}else t.push(" Avg Time: N/A");return t}function he(s,e="─"){return e.repeat(s)}function ni(s,e,t){const a=Math.max(0,e-s.length-2),i=Math.floor(a/2),n=a-i,r=he(e),o=t?S.bright+S.cyan+s+S.reset:s;return`
|
|
49
|
+
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`)}for(const r of s.seen.entries()){const o=r[1];if(e===r[0]){n(r);continue}if(s.external){const c=s.external.registry.get(r[0])?.id;if(e!==r[0]&&c){n(r);continue}}if(s.metadataRegistry.get(r[0])?.id){n(r);continue}if(o.cycle){n(r);continue}if(o.count>1&&s.reused==="ref"){n(r);continue}}}function hn(s,e){const t=s.seen.get(e);if(!t)throw new Error("Unprocessed schema. This is a bug in Zod.");const a=r=>{const o=s.seen.get(r);if(o.ref===null)return;const l=o.def??o.schema,c={...l},u=o.ref;if(o.ref=null,u){a(u);const h=s.seen.get(u),p=h.schema;if(p.$ref&&(s.target==="draft-07"||s.target==="draft-04"||s.target==="openapi-3.0")?(l.allOf=l.allOf??[],l.allOf.push(p)):Object.assign(l,p),Object.assign(l,c),r._zod.parent===u)for(const m in l)m==="$ref"||m==="allOf"||m in c||delete l[m];if(p.$ref&&h.def)for(const m in l)m==="$ref"||m==="allOf"||m in h.def&&JSON.stringify(l[m])===JSON.stringify(h.def[m])&&delete l[m]}const d=r._zod.parent;if(d&&d!==u){a(d);const h=s.seen.get(d);if(h?.schema.$ref&&(l.$ref=h.schema.$ref,h.def))for(const p in l)p==="$ref"||p==="allOf"||p in h.def&&JSON.stringify(l[p])===JSON.stringify(h.def[p])&&delete l[p]}s.override({zodSchema:r,jsonSchema:l,path:o.path??[]})};for(const r of[...s.seen.entries()].reverse())a(r[0]);const i={};if(s.target==="draft-2020-12"?i.$schema="https://json-schema.org/draft/2020-12/schema":s.target==="draft-07"?i.$schema="http://json-schema.org/draft-07/schema#":s.target==="draft-04"?i.$schema="http://json-schema.org/draft-04/schema#":s.target,s.external?.uri){const r=s.external.registry.get(e)?.id;if(!r)throw new Error("Schema is missing an `id` property");i.$id=s.external.uri(r)}Object.assign(i,t.def??t.schema);const n=s.external?.defs??{};for(const r of s.seen.entries()){const o=r[1];o.def&&o.defId&&(n[o.defId]=o.def)}s.external||Object.keys(n).length>0&&(s.target==="draft-2020-12"?i.$defs=n:i.definitions=n);try{const r=JSON.parse(JSON.stringify(i));return Object.defineProperty(r,"~standard",{value:{...e["~standard"],jsonSchema:{input:Lt(e,"input",s.processors),output:Lt(e,"output",s.processors)}},enumerable:!1,writable:!1}),r}catch{throw new Error("Error converting schema to JSON.")}}function ce(s,e){const t=e??{seen:new Set};if(t.seen.has(s))return!1;t.seen.add(s);const a=s._zod.def;if(a.type==="transform")return!0;if(a.type==="array")return ce(a.element,t);if(a.type==="set")return ce(a.valueType,t);if(a.type==="lazy")return ce(a.getter(),t);if(a.type==="promise"||a.type==="optional"||a.type==="nonoptional"||a.type==="nullable"||a.type==="readonly"||a.type==="default"||a.type==="prefault")return ce(a.innerType,t);if(a.type==="intersection")return ce(a.left,t)||ce(a.right,t);if(a.type==="record"||a.type==="map")return ce(a.keyType,t)||ce(a.valueType,t);if(a.type==="pipe")return ce(a.in,t)||ce(a.out,t);if(a.type==="object"){for(const i in a.shape)if(ce(a.shape[i],t))return!0;return!1}if(a.type==="union"){for(const i of a.options)if(ce(i,t))return!0;return!1}if(a.type==="tuple"){for(const i of a.items)if(ce(i,t))return!0;return!!(a.rest&&ce(a.rest,t))}return!1}const uu=(s,e={})=>t=>{const a=un({...t,processors:e});return K(s,a),dn(a,s),hn(a,s)},Lt=(s,e,t={})=>a=>{const{libraryOptions:i,target:n}=a??{},r=un({...i??{},target:n,io:e,processors:t});return K(s,r),dn(r,s),hn(r,s)},du={guid:"uuid",url:"uri",datetime:"date-time",json_string:"json-string",regex:""},hu=(s,e,t,a)=>{const i=t;i.type="string";const{minimum:n,maximum:r,format:o,patterns:l,contentEncoding:c}=s._zod.bag;if(typeof n=="number"&&(i.minLength=n),typeof r=="number"&&(i.maxLength=r),o&&(i.format=du[o]??o,i.format===""&&delete i.format,o==="time"&&delete i.format),c&&(i.contentEncoding=c),l&&l.size>0){const u=[...l];u.length===1?i.pattern=u[0].source:u.length>1&&(i.allOf=[...u.map(d=>({...e.target==="draft-07"||e.target==="draft-04"||e.target==="openapi-3.0"?{type:"string"}:{},pattern:d.source}))])}},pu=(s,e,t,a)=>{const i=t,{minimum:n,maximum:r,format:o,multipleOf:l,exclusiveMaximum:c,exclusiveMinimum:u}=s._zod.bag;typeof o=="string"&&o.includes("int")?i.type="integer":i.type="number",typeof u=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(i.minimum=u,i.exclusiveMinimum=!0):i.exclusiveMinimum=u),typeof n=="number"&&(i.minimum=n,typeof u=="number"&&e.target!=="draft-04"&&(u>=n?delete i.minimum:delete i.exclusiveMinimum)),typeof c=="number"&&(e.target==="draft-04"||e.target==="openapi-3.0"?(i.maximum=c,i.exclusiveMaximum=!0):i.exclusiveMaximum=c),typeof r=="number"&&(i.maximum=r,typeof c=="number"&&e.target!=="draft-04"&&(c<=r?delete i.maximum:delete i.exclusiveMaximum)),typeof l=="number"&&(i.multipleOf=l)},mu=(s,e,t,a)=>{t.type="boolean"},gu=(s,e,t,a)=>{t.not={}},fu=(s,e,t,a)=>{},yu=(s,e,t,a)=>{const i=s._zod.def,n=qs(i.entries);n.every(r=>typeof r=="number")&&(t.type="number"),n.every(r=>typeof r=="string")&&(t.type="string"),t.enum=n},vu=(s,e,t,a)=>{if(e.unrepresentable==="throw")throw new Error("Custom types cannot be represented in JSON Schema")},bu=(s,e,t,a)=>{if(e.unrepresentable==="throw")throw new Error("Transforms cannot be represented in JSON Schema")},wu=(s,e,t,a)=>{const i=t,n=s._zod.def,{minimum:r,maximum:o}=s._zod.bag;typeof r=="number"&&(i.minItems=r),typeof o=="number"&&(i.maxItems=o),i.type="array",i.items=K(n.element,e,{...a,path:[...a.path,"items"]})},Su=(s,e,t,a)=>{const i=t,n=s._zod.def;i.type="object",i.properties={};const r=n.shape;for(const c in r)i.properties[c]=K(r[c],e,{...a,path:[...a.path,"properties",c]});const o=new Set(Object.keys(r)),l=new Set([...o].filter(c=>{const u=n.shape[c]._zod;return e.io==="input"?u.optin===void 0:u.optout===void 0}));l.size>0&&(i.required=Array.from(l)),n.catchall?._zod.def.type==="never"?i.additionalProperties=!1:n.catchall?n.catchall&&(i.additionalProperties=K(n.catchall,e,{...a,path:[...a.path,"additionalProperties"]})):e.io==="output"&&(i.additionalProperties=!1)},_u=(s,e,t,a)=>{const i=s._zod.def,n=i.inclusive===!1,r=i.options.map((o,l)=>K(o,e,{...a,path:[...a.path,n?"oneOf":"anyOf",l]}));n?t.oneOf=r:t.anyOf=r},Au=(s,e,t,a)=>{const i=s._zod.def,n=K(i.left,e,{...a,path:[...a.path,"allOf",0]}),r=K(i.right,e,{...a,path:[...a.path,"allOf",1]}),o=c=>"allOf"in c&&Object.keys(c).length===1,l=[...o(n)?n.allOf:[n],...o(r)?r.allOf:[r]];t.allOf=l},ku=(s,e,t,a)=>{const i=t,n=s._zod.def;i.type="object";const r=n.keyType,l=r._zod.bag?.patterns;if(n.mode==="loose"&&l&&l.size>0){const u=K(n.valueType,e,{...a,path:[...a.path,"patternProperties","*"]});i.patternProperties={};for(const d of l)i.patternProperties[d.source]=u}else(e.target==="draft-07"||e.target==="draft-2020-12")&&(i.propertyNames=K(n.keyType,e,{...a,path:[...a.path,"propertyNames"]})),i.additionalProperties=K(n.valueType,e,{...a,path:[...a.path,"additionalProperties"]});const c=r._zod.values;if(c){const u=[...c].filter(d=>typeof d=="string"||typeof d=="number");u.length>0&&(i.required=u)}},Tu=(s,e,t,a)=>{const i=s._zod.def,n=K(i.innerType,e,a),r=e.seen.get(s);e.target==="openapi-3.0"?(r.ref=i.innerType,t.nullable=!0):t.anyOf=[n,{type:"null"}]},Cu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType},Iu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,t.default=JSON.parse(JSON.stringify(i.defaultValue))},Eu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,e.io==="input"&&(t._prefault=JSON.parse(JSON.stringify(i.defaultValue)))},Mu=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType;let r;try{r=i.catchValue(void 0)}catch{throw new Error("Dynamic catch values are not supported in JSON Schema")}t.default=r},Du=(s,e,t,a)=>{const i=s._zod.def,n=e.io==="input"?i.in._zod.def.type==="transform"?i.out:i.in:i.out;K(n,e,a);const r=e.seen.get(s);r.ref=n},Ru=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType,t.readOnly=!0},pn=(s,e,t,a)=>{const i=s._zod.def;K(i.innerType,e,a);const n=e.seen.get(s);n.ref=i.innerType},Pu=b("ZodISODateTime",(s,e)=>{$l.init(s,e),j.init(s,e)});function xu(s){return Hc(Pu,s)}const Nu=b("ZodISODate",(s,e)=>{Ll.init(s,e),j.init(s,e)});function $u(s){return Wc(Nu,s)}const Lu=b("ZodISOTime",(s,e)=>{Ol.init(s,e),j.init(s,e)});function Ou(s){return zc(Lu,s)}const Bu=b("ZodISODuration",(s,e)=>{Bl.init(s,e),j.init(s,e)});function Fu(s){return qc(Bu,s)}const Hu=(s,e)=>{Vs.init(s,e),s.name="ZodError",Object.defineProperties(s,{format:{value:t=>Eo(s,t)},flatten:{value:t=>Io(s,t)},addIssue:{value:t=>{s.issues.push(t),s.message=JSON.stringify(s.issues,ua,2)}},addIssues:{value:t=>{s.issues.push(...t),s.message=JSON.stringify(s.issues,ua,2)}},isEmpty:{get(){return s.issues.length===0}}})},ve=b("ZodError",Hu,{Parent:Error}),Wu=Oa(ve),zu=Ba(ve),qu=Ut(ve),Uu=Gt(ve),Gu=Ro(ve),ju=Po(ve),Vu=xo(ve),Yu=No(ve),Zu=$o(ve),Xu=Lo(ve),Ku=Oo(ve),Ju=Bo(ve),Z=b("ZodType",(s,e)=>(Y.init(s,e),Object.assign(s["~standard"],{jsonSchema:{input:Lt(s,"input"),output:Lt(s,"output")}}),s.toJSONSchema=uu(s,{}),s.def=e,s.type=e.type,Object.defineProperty(s,"_def",{value:e}),s.check=(...t)=>s.clone($e(e,{checks:[...e.checks??[],...t.map(a=>typeof a=="function"?{_zod:{check:a,def:{check:"custom"},onattach:[]}}:a)]}),{parent:!0}),s.with=s.check,s.clone=(t,a)=>Le(s,t,a),s.brand=()=>s,s.register=((t,a)=>(t.add(s,a),s)),s.parse=(t,a)=>Wu(s,t,a,{callee:s.parse}),s.safeParse=(t,a)=>qu(s,t,a),s.parseAsync=async(t,a)=>zu(s,t,a,{callee:s.parseAsync}),s.safeParseAsync=async(t,a)=>Uu(s,t,a),s.spa=s.safeParseAsync,s.encode=(t,a)=>Gu(s,t,a),s.decode=(t,a)=>ju(s,t,a),s.encodeAsync=async(t,a)=>Vu(s,t,a),s.decodeAsync=async(t,a)=>Yu(s,t,a),s.safeEncode=(t,a)=>Zu(s,t,a),s.safeDecode=(t,a)=>Xu(s,t,a),s.safeEncodeAsync=async(t,a)=>Ku(s,t,a),s.safeDecodeAsync=async(t,a)=>Ju(s,t,a),s.refine=(t,a)=>s.check(Ud(t,a)),s.superRefine=t=>s.check(Gd(t)),s.overwrite=t=>s.check(et(t)),s.optional=()=>Qi(s),s.exactOptional=()=>Rd(s),s.nullable=()=>es(s),s.nullish=()=>Qi(es(s)),s.nonoptional=t=>Od(s,t),s.array=()=>ne(s),s.or=t=>Ha([s,t]),s.and=t=>Cd(s,t),s.transform=t=>ts(s,Md(t)),s.default=t=>Nd(s,t),s.prefault=t=>Ld(s,t),s.catch=t=>Fd(s,t),s.pipe=t=>ts(s,t),s.readonly=()=>zd(s),s.describe=t=>{const a=s.clone();return nt.add(a,{description:t}),a},Object.defineProperty(s,"description",{get(){return nt.get(s)?.description},configurable:!0}),s.meta=(...t)=>{if(t.length===0)return nt.get(s);const a=s.clone();return nt.add(a,t[0]),a},s.isOptional=()=>s.safeParse(void 0).success,s.isNullable=()=>s.safeParse(null).success,s.apply=t=>t(s),s)),mn=b("_ZodString",(s,e)=>{Fa.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>hu(s,a,i);const t=s._zod.bag;s.format=t.format??null,s.minLength=t.minimum??null,s.maxLength=t.maximum??null,s.regex=(...a)=>s.check(Zc(...a)),s.includes=(...a)=>s.check(Jc(...a)),s.startsWith=(...a)=>s.check(Qc(...a)),s.endsWith=(...a)=>s.check(eu(...a)),s.min=(...a)=>s.check($t(...a)),s.max=(...a)=>s.check(ln(...a)),s.length=(...a)=>s.check(cn(...a)),s.nonempty=(...a)=>s.check($t(1,...a)),s.lowercase=a=>s.check(Xc(a)),s.uppercase=a=>s.check(Kc(a)),s.trim=()=>s.check(au()),s.normalize=(...a)=>s.check(tu(...a)),s.toLowerCase=()=>s.check(iu()),s.toUpperCase=()=>s.check(su()),s.slugify=()=>s.check(nu())}),Qu=b("ZodString",(s,e)=>{Fa.init(s,e),mn.init(s,e),s.email=t=>s.check(bc(ed,t)),s.url=t=>s.check(kc(td,t)),s.jwt=t=>s.check(Fc(fd,t)),s.emoji=t=>s.check(Tc(ad,t)),s.guid=t=>s.check(ji(Xi,t)),s.uuid=t=>s.check(wc(Tt,t)),s.uuidv4=t=>s.check(Sc(Tt,t)),s.uuidv6=t=>s.check(_c(Tt,t)),s.uuidv7=t=>s.check(Ac(Tt,t)),s.nanoid=t=>s.check(Cc(id,t)),s.guid=t=>s.check(ji(Xi,t)),s.cuid=t=>s.check(Ic(sd,t)),s.cuid2=t=>s.check(Ec(nd,t)),s.ulid=t=>s.check(Mc(rd,t)),s.base64=t=>s.check(Lc(pd,t)),s.base64url=t=>s.check(Oc(md,t)),s.xid=t=>s.check(Dc(od,t)),s.ksuid=t=>s.check(Rc(ld,t)),s.ipv4=t=>s.check(Pc(cd,t)),s.ipv6=t=>s.check(xc(ud,t)),s.cidrv4=t=>s.check(Nc(dd,t)),s.cidrv6=t=>s.check($c(hd,t)),s.e164=t=>s.check(Bc(gd,t)),s.datetime=t=>s.check(xu(t)),s.date=t=>s.check($u(t)),s.time=t=>s.check(Ou(t)),s.duration=t=>s.check(Fu(t))});function T(s){return vc(Qu,s)}const j=b("ZodStringFormat",(s,e)=>{G.init(s,e),mn.init(s,e)}),ed=b("ZodEmail",(s,e)=>{Cl.init(s,e),j.init(s,e)}),Xi=b("ZodGUID",(s,e)=>{kl.init(s,e),j.init(s,e)}),Tt=b("ZodUUID",(s,e)=>{Tl.init(s,e),j.init(s,e)}),td=b("ZodURL",(s,e)=>{Il.init(s,e),j.init(s,e)}),ad=b("ZodEmoji",(s,e)=>{El.init(s,e),j.init(s,e)}),id=b("ZodNanoID",(s,e)=>{Ml.init(s,e),j.init(s,e)}),sd=b("ZodCUID",(s,e)=>{Dl.init(s,e),j.init(s,e)}),nd=b("ZodCUID2",(s,e)=>{Rl.init(s,e),j.init(s,e)}),rd=b("ZodULID",(s,e)=>{Pl.init(s,e),j.init(s,e)}),od=b("ZodXID",(s,e)=>{xl.init(s,e),j.init(s,e)}),ld=b("ZodKSUID",(s,e)=>{Nl.init(s,e),j.init(s,e)}),cd=b("ZodIPv4",(s,e)=>{Fl.init(s,e),j.init(s,e)}),ud=b("ZodIPv6",(s,e)=>{Hl.init(s,e),j.init(s,e)}),dd=b("ZodCIDRv4",(s,e)=>{Wl.init(s,e),j.init(s,e)}),hd=b("ZodCIDRv6",(s,e)=>{zl.init(s,e),j.init(s,e)}),pd=b("ZodBase64",(s,e)=>{ql.init(s,e),j.init(s,e)}),md=b("ZodBase64URL",(s,e)=>{Gl.init(s,e),j.init(s,e)}),gd=b("ZodE164",(s,e)=>{jl.init(s,e),j.init(s,e)}),fd=b("ZodJWT",(s,e)=>{Yl.init(s,e),j.init(s,e)}),gn=b("ZodNumber",(s,e)=>{sn.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>pu(s,a,i),s.gt=(a,i)=>s.check(Yi(a,i)),s.gte=(a,i)=>s.check(Qt(a,i)),s.min=(a,i)=>s.check(Qt(a,i)),s.lt=(a,i)=>s.check(Vi(a,i)),s.lte=(a,i)=>s.check(Jt(a,i)),s.max=(a,i)=>s.check(Jt(a,i)),s.int=a=>s.check(Ki(a)),s.safe=a=>s.check(Ki(a)),s.positive=a=>s.check(Yi(0,a)),s.nonnegative=a=>s.check(Qt(0,a)),s.negative=a=>s.check(Vi(0,a)),s.nonpositive=a=>s.check(Jt(0,a)),s.multipleOf=(a,i)=>s.check(Zi(a,i)),s.step=(a,i)=>s.check(Zi(a,i)),s.finite=()=>s;const t=s._zod.bag;s.minValue=Math.max(t.minimum??Number.NEGATIVE_INFINITY,t.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,s.maxValue=Math.min(t.maximum??Number.POSITIVE_INFINITY,t.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,s.isInt=(t.format??"").includes("int")||Number.isSafeInteger(t.multipleOf??.5),s.isFinite=!0,s.format=t.format??null});function A(s){return Uc(gn,s)}const yd=b("ZodNumberFormat",(s,e)=>{Zl.init(s,e),gn.init(s,e)});function Ki(s){return Gc(yd,s)}const vd=b("ZodBoolean",(s,e)=>{Xl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>mu(s,t,a)});function ha(s){return jc(vd,s)}const bd=b("ZodUnknown",(s,e)=>{Kl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>fu()});function Ji(){return Vc(bd)}const wd=b("ZodNever",(s,e)=>{Jl.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>gu(s,t,a)});function Sd(s){return Yc(wd,s)}const _d=b("ZodArray",(s,e)=>{Ql.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>wu(s,t,a,i),s.element=e.element,s.min=(t,a)=>s.check($t(t,a)),s.nonempty=t=>s.check($t(1,t)),s.max=(t,a)=>s.check(ln(t,a)),s.length=(t,a)=>s.check(cn(t,a)),s.unwrap=()=>s.element});function ne(s,e){return ru(_d,s,e)}const Ad=b("ZodObject",(s,e)=>{tc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Su(s,t,a,i),F(s,"shape",()=>e.shape),s.keyof=()=>It(Object.keys(s._zod.def.shape)),s.catchall=t=>s.clone({...s._zod.def,catchall:t}),s.passthrough=()=>s.clone({...s._zod.def,catchall:Ji()}),s.loose=()=>s.clone({...s._zod.def,catchall:Ji()}),s.strict=()=>s.clone({...s._zod.def,catchall:Sd()}),s.strip=()=>s.clone({...s._zod.def,catchall:void 0}),s.extend=t=>_o(s,t),s.safeExtend=t=>Ao(s,t),s.merge=t=>ko(s,t),s.pick=t=>wo(s,t),s.omit=t=>So(s,t),s.partial=(...t)=>To(fn,s,t[0]),s.required=(...t)=>Co(yn,s,t[0])});function W(s,e){const t={type:"object",shape:s??{},...M(e)};return new Ad(t)}const kd=b("ZodUnion",(s,e)=>{ac.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>_u(s,t,a,i),s.options=e.options});function Ha(s,e){return new kd({type:"union",options:s,...M(e)})}const Td=b("ZodIntersection",(s,e)=>{ic.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Au(s,t,a,i)});function Cd(s,e){return new Td({type:"intersection",left:s,right:e})}const Id=b("ZodRecord",(s,e)=>{sc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>ku(s,t,a,i),s.keyType=e.keyType,s.valueType=e.valueType});function Ct(s,e,t){return new Id({type:"record",keyType:s,valueType:e,...M(t)})}const pa=b("ZodEnum",(s,e)=>{nc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(a,i,n)=>yu(s,a,i),s.enum=e.entries,s.options=Object.values(e.entries);const t=new Set(Object.keys(e.entries));s.extract=(a,i)=>{const n={};for(const r of a)if(t.has(r))n[r]=e.entries[r];else throw new Error(`Key ${r} not found in enum`);return new pa({...e,checks:[],...M(i),entries:n})},s.exclude=(a,i)=>{const n={...e.entries};for(const r of a)if(t.has(r))delete n[r];else throw new Error(`Key ${r} not found in enum`);return new pa({...e,checks:[],...M(i),entries:n})}});function It(s,e){const t=Array.isArray(s)?Object.fromEntries(s.map(a=>[a,a])):s;return new pa({type:"enum",entries:t,...M(e)})}const Ed=b("ZodTransform",(s,e)=>{rc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>bu(s,t),s._zod.parse=(t,a)=>{if(a.direction==="backward")throw new Ws(s.constructor.name);t.addIssue=n=>{if(typeof n=="string")t.issues.push(ut(n,t.value,e));else{const r=n;r.fatal&&(r.continue=!1),r.code??(r.code="custom"),r.input??(r.input=t.value),r.inst??(r.inst=s),t.issues.push(ut(r))}};const i=e.transform(t.value,t);return i instanceof Promise?i.then(n=>(t.value=n,t)):(t.value=i,t)}});function Md(s){return new Ed({type:"transform",transform:s})}const fn=b("ZodOptional",(s,e)=>{on.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>pn(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Qi(s){return new fn({type:"optional",innerType:s})}const Dd=b("ZodExactOptional",(s,e)=>{oc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>pn(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Rd(s){return new Dd({type:"optional",innerType:s})}const Pd=b("ZodNullable",(s,e)=>{lc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Tu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function es(s){return new Pd({type:"nullable",innerType:s})}const xd=b("ZodDefault",(s,e)=>{cc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Iu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType,s.removeDefault=s.unwrap});function Nd(s,e){return new xd({type:"default",innerType:s,get defaultValue(){return typeof e=="function"?e():Gs(e)}})}const $d=b("ZodPrefault",(s,e)=>{uc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Eu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Ld(s,e){return new $d({type:"prefault",innerType:s,get defaultValue(){return typeof e=="function"?e():Gs(e)}})}const yn=b("ZodNonOptional",(s,e)=>{dc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Cu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function Od(s,e){return new yn({type:"nonoptional",innerType:s,...M(e)})}const Bd=b("ZodCatch",(s,e)=>{hc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Mu(s,t,a,i),s.unwrap=()=>s._zod.def.innerType,s.removeCatch=s.unwrap});function Fd(s,e){return new Bd({type:"catch",innerType:s,catchValue:typeof e=="function"?e:()=>e})}const Hd=b("ZodPipe",(s,e)=>{pc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Du(s,t,a,i),s.in=e.in,s.out=e.out});function ts(s,e){return new Hd({type:"pipe",in:s,out:e})}const Wd=b("ZodReadonly",(s,e)=>{mc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>Ru(s,t,a,i),s.unwrap=()=>s._zod.def.innerType});function zd(s){return new Wd({type:"readonly",innerType:s})}const qd=b("ZodCustom",(s,e)=>{gc.init(s,e),Z.init(s,e),s._zod.processJSONSchema=(t,a,i)=>vu(s,t)});function Ud(s,e={}){return ou(qd,s,e)}function Gd(s){return lu(s)}const jd=Re.map(s=>s),vn=W({id:T(),uuid:T(),playlist_index:A().nonnegative(),chain_name:T(),token_address:T().optional(),token_id:T().optional(),tx_id:T().optional(),platform:T(),title:T(),artist:T(),description:T().optional(),album:T().optional(),image_url:T().url(),image_thumb_url:T().url().optional(),audio_url:T().url(),audio_url_lossless:T().url().optional(),duration:A().nonnegative(),genre:T(),tags:ne(T()),bpm:A().positive().optional(),key:T().optional(),attributes:Ct(T(),Ha([T(),A()])).optional()}).refine(s=>s.chain_name==="AR"?!!s.tx_id:!!s.token_address&&!!s.token_id,{message:"AR chain tracks must have tx_id; other chains must have token_address and token_id",path:["chain_name"]}),Vd=W({name:T(),description:T().optional(),image:T().url(),creator:T(),genre:T().optional(),tags:ne(T()).optional(),tracks:ne(vn)}),Yd=W({bass_dominance:A().min(0).max(1),mid_dominance:A().min(0).max(1),treble_dominance:A().min(0).max(1),average_amplitude:A().min(0).max(1),spectral_centroid:A().optional(),spectral_rolloff:A().optional(),zero_crossing_rate:A().optional(),color_palette:W({colors:ne(T()),primary_color:T(),secondary_color:T().optional(),accent_color:T().optional(),brightness:A().min(0).max(1),saturation:A().min(0).max(1),is_monochrome:ha()}).optional(),analysis_metadata:W({duration_analyzed:A().positive(),full_buffer_analyzed:ha(),sample_positions:ne(A()),analyzed_at:T()})}),ma=W({STR:A().min(1).max(20),DEX:A().min(1).max(20),CON:A().min(1).max(20),INT:A().min(1).max(20),WIS:A().min(1).max(20),CHA:A().min(1).max(20)}),Zd=W({name:T(),race:It(["Human","Elf","Dwarf","Halfling","Dragonborn","Gnome","Half-Elf","Half-Orc","Tiefling"]),class:It(jd),level:A().min(1).max(20),ability_scores:ma,ability_modifiers:ma,proficiency_bonus:A().min(2).max(6),hp:W({current:A().nonnegative(),max:A().positive(),temp:A().nonnegative()}),armor_class:A().positive(),initiative:A(),speed:A().positive(),skills:Ct(T(),It(["none","proficient","expertise"])),saving_throws:Ct(T(),ha()),racial_traits:ne(T()),class_features:ne(T()),spells:W({spell_slots:Ct(T(),W({total:A().nonnegative(),used:A().nonnegative()})),known_spells:ne(T()),cantrips:ne(T())}).optional(),equipment:W({weapons:ne(T()),armor:ne(T()),items:ne(T())}).optional(),appearance:W({body_type:T(),skin_tone:T(),hair_style:T(),hair_color:T(),eye_color:T(),facial_features:ne(T()),primary_color:T().optional(),secondary_color:T().optional(),aura_color:T().optional()}).optional(),xp:W({current:A().nonnegative(),next_level:A().positive()}),seed:T(),generated_at:T()});function Xd(s){if(!s||!s.metadata)return null;if(typeof s.metadata=="string")try{const e=JSON.parse(s.metadata);return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:null}catch{return null}return typeof s.metadata=="object"&&!Array.isArray(s.metadata)?s.metadata:null}function bn(s){const e={stems:[],mixes:[],hasExtras:!1};if(!s)return e;const t=th(s),a=ah(s),i=typeof s.vrm=="string"?s.vrm:void 0,n=Jd(s.lyrics),r=as(s.visualizer),o=as(s.video),l=Qd(s.merch),c=eh(s.credits),u=typeof s.midi=="string"?s.midi:void 0,d=typeof s.step_mania=="string"?s.step_mania:void 0,h=typeof s.clone_hero=="string"?s.clone_hero:void 0,p=typeof s.external_url=="string"?s.external_url:void 0;return{hasExtras:t.length>0||a.length>0||!!i||!!n||!!r||!!o||!!l||!!c||!!u||!!d||!!h||!!p,...t.length>0?{stems:t}:{},...a.length>0?{mixes:a}:{},...i?{vrm:i}:{},...n?{lyrics:n}:{},...r?{visualizer:r}:{},...o?{video:o}:{},...l?{merch:l}:{},...c?{credits:c}:{},...u?{midi:u}:{},...d?{step_mania:d}:{},...h?{clone_hero:h}:{},...p?{external_url:p}:{}}}const Kd=["audio/wav","audio/x-wav","audio/flac"];function wn(s,e,t){const a=e?.["Selected Mix"],i=typeof s=="string"&&s||typeof a=="string"&&a||null;if(!i||i==="default")return null;const n=(t??[]).filter(c=>c.name===i);if(n.length===0)return null;const r=c=>Kd.includes((c.mime_type??"").toLowerCase()),o=n.find(r),l=n.find(c=>!r(c))??n[0];return{name:i,...l?.uri?{audio_url:l.uri}:{},...o?.uri&&o.uri!==l?.uri?{audio_url_lossless:o.uri}:{}}}function Jd(s){if(s){if(typeof s=="string")return{text:s};if(typeof s=="object"&&!Array.isArray(s)){const e=s;return{text:typeof e.text=="string"?e.text:void 0}}}}function as(s){if(!s||typeof s!="object"||Array.isArray(s))return;const e=s;if(typeof e.uri=="string")return{uri:e.uri,mime_type:typeof e.mime_type=="string"?e.mime_type:void 0}}function Qd(s){if(!s||typeof s!="object"||Array.isArray(s))return;const e=s;if(typeof e.uri=="string")return{uri:e.uri,mime_type:typeof e.mime_type=="string"?e.mime_type:void 0,type:typeof e.type=="string"?e.type:void 0}}function eh(s){if(!Array.isArray(s))return;const e=[];for(const t of s)if(t&&typeof t=="object"){const a=t;typeof a.name=="string"&&typeof a.credit=="string"&&e.push({name:a.name,credit:a.credit})}return e.length>0?e:void 0}function th(s){const e=s.stems;if(!Array.isArray(e))return[];const t=[];for(const a of e)if(a&&typeof a=="object"){const i=a;t.push({name:typeof i.name=="string"?i.name:"",uri:typeof i.uri=="string"?i.uri:void 0,mime_type:typeof i.mime_type=="string"?i.mime_type:void 0})}return t}function ah(s){const e=s.mixes;if(!Array.isArray(e))return[];const t=[];for(const a of e)if(a&&typeof a=="object"){const i=a,n=ih(i.name),r=sh(i.conditions);t.push({name:n||"",uri:typeof i.uri=="string"?i.uri:void 0,mime_type:typeof i.mime_type=="string"?i.mime_type:void 0,conditions:r})}return t}function ih(s){if(typeof s=="string")return s;if(s&&typeof s=="object"&&!Array.isArray(s)){const e=s;if(typeof e.value=="string")return e.value}return""}function sh(s){if(!Array.isArray(s))return[];const e=[];for(const t of s)if(t&&typeof t=="object"){const a=t;typeof a.type=="string"&&typeof a.value=="string"&&e.push({type:a.type,value:a.value})}return e}function nh(s,e){const t=[],a=new Date;for(const i of s.mixes||[]){if(i.conditions.length===0){t.push({mix:i,conditions:[],allMet:!0,unmetConditions:[]});continue}const n=[],r=[];for(const o of i.conditions){const l=rh(o,e,a);n.push(l),l.met||r.push(l)}t.push({mix:i,conditions:n,allMet:r.length===0,unmetConditions:r})}return t}function rh(s,e,t=new Date){const{type:a,value:i}=s,n=e?.environment,r=e?.appState;switch(a){case"weather":{const o=n?.weather?.weatherType||"",l=o.toLowerCase()===i.toLowerCase();return{type:a,value:i,met:l,reason:l?`Weather is ${o}`:`Weather is ${o} (need ${i})`}}case"day":{const l=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][t.getDay()],c=l.toLowerCase()===i.toLowerCase();return{type:a,value:i,met:c,reason:c?`Today is ${l}`:`Today is ${l} (need ${i})`}}case"start_time":{const o=t.getHours()*60+t.getMinutes(),l=is(i),c=o>=l;return{type:a,value:i,met:c,reason:c?`Time is after ${i}`:`Time is before ${i}`}}case"end_time":{const o=t.getHours()*60+t.getMinutes(),l=is(i),c=o<l;return{type:a,value:i,met:c,reason:c?`Time is before ${i}`:`Time is after ${i}`}}case"min_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=l>=o;return{type:a,value:i,met:c,reason:c?`Played ${l} times (need ${o})`:`Played ${l} times (need ${o})`}}case"max_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=l<=o;return{type:a,value:i,met:c,reason:c?`Played ${l} times (max ${o})`:`Played ${l} times (exceeds max ${o})`}}case"every_x_plays":{const o=parseInt(i,10),l=r?.playCount??0,c=o>0&&l>0&&l%o===0;return{type:a,value:i,met:c,reason:c?`Play #${l} (every ${o} plays)`:`Play #${l} (not a multiple of ${o})`}}case"altitude":{const o=n?.geolocation?.altitude;if(o==null)return{type:a,value:i,met:!1,reason:"Altitude unavailable"};const l=oh(i),c=lh(o,l.operator,l.threshold);return{type:a,value:i,met:c,reason:c?`Altitude ${o}m ${l.operator} ${l.threshold}m`:`Altitude ${o}m (need ${l.operator} ${l.threshold}m)`}}case"favorite":{const o=r?.isFavorite??!1,l=i.toLowerCase()==="true",c=o===l;return{type:a,value:i,met:c,reason:c?o?"Track is favorited":"Track is not favorited":o?"Track is favorited (need unfavorited)":"Track is not favorited (need favorited)"}}case"birthday":{if(!r?.userBirthday)return{type:a,value:i,met:!1,reason:"Birthday not set"};const l=`${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`===r.userBirthday;return{type:a,value:i,met:l,reason:l?"It's your birthday!":`Not your birthday (need ${r.userBirthday})`}}case"weight":return{type:a,value:i,met:!0,reason:`Weight ${i} (used for random selection, always available)`};default:return{type:a,value:i,met:!0,reason:`Custom condition: ${a} = ${i}`}}}function is(s){const e=s.split(":");if(e.length!==2)return 0;const t=parseInt(e[0],10),a=parseInt(e[1],10);return isNaN(t)||isNaN(a)?0:t*60+a}function oh(s){const e=s.match(/^(>=|<=|>|<|=)?\s*(\d+(?:\.\d+)?)\s*$/);return e?{operator:e[1]||">=",threshold:parseFloat(e[2])}:{operator:">=",threshold:parseFloat(s)||0}}function lh(s,e,t){switch(e){case">":return s>t;case">=":return s>=t;case"<":return s<t;case"<=":return s<=t;case"=":case"==":return s===t;default:return s>=t}}class Sn{generateName(e,t,a,i,n=!1){const r=this.cleanTitle(t.title),o=n?e:`${e}-${Date.now()}-${Math.random()}`,l=new Se(o);switch(this.selectFormat(l)){case"class_title":return this.formatClassTitle(r,i,l);case"adjective_construct":return this.formatAdjectiveConstruct(r,t.genre,a,l);case"clan_construct":return this.formatClanConstruct(r,t.artist,l);case"descriptive_epithet":return this.formatDescriptiveEpithet(r,i,a,l);case"compound_adjective":return this.formatCompoundAdjective(r,t.genre,a,l);case"artist_inspired":return this.formatArtistInspired(r,t.artist,i,l);case"mononym_subtitle":return this.formatMononymSubtitle(r,a,t.genre,l);default:return r}}cleanTitle(e){let t=e;return t=t.replace(/\s*[([].*?(video|official|remix|mix|edit|feat|ft\.|version|remaster).*?[)\]]/gi,""),t=t.replace(/^\s*\d+[\s.-]+/,""),t=t.replace(/\.(mp3|wav|flac|m4a)$/i,""),t.trim()}selectFormat(e){const t=e.random();return t<.2?"class_title":t<.4?"adjective_construct":t<.5?"clan_construct":t<.7?"descriptive_epithet":t<.85?"compound_adjective":t<.95?"artist_inspired":"mononym_subtitle"}formatClassTitle(e,t,a){return`${e} the ${t}`}formatAdjectiveConstruct(e,t,a,i){const n=this.findGenreKey(t),r=Ae.adjectives[n]||Ae.adjectives.default,o=this.calculateBalancedAdjectiveWeights(a),l=i.weightedChoice(o),c=r[l];return`${i.randomChoice(c)} ${e}`}formatClanConstruct(e,t,a){return`${e} of ${t}`}formatDescriptiveEpithet(e,t,a,i){const n=i.randomChoice(Ae.descriptors),r=Ae.classAspects[t]||["Wanderer","Seeker","Sage","Hero"],o=i.randomChoice(r);return`${e}, the ${n} ${o}`}formatCompoundAdjective(e,t,a,i){const n=i.randomChoice(Ae.prefixes),r=i.randomChoice(Ae.suffixes);return`${n}-${r} ${e}`}formatArtistInspired(e,t,a,i){const n=t.split(" ")[0],r=i.randomChoice(Ae.occupations),o=`${n}${r}`,l=i.randomChoice(Ae.realms);return`${o} of the ${l}`}formatMononymSubtitle(e,t,a,i){const n=e.split(" "),r=n[0];let o;return n.length>1?o=n.slice(1).join(" "):o=i.randomChoice(Ae.subtitlePrefixes),`${r} [${o}]`}calculateBalancedAdjectiveWeights(e){const a={bass:e.bass_dominance,treble:e.treble_dominance,mid:e.mid_dominance,quiet:e.average_amplitude<.4?.5:0,loud:e.average_amplitude>.6?.5:0};return[["bass",1+a.bass],["treble",1+a.treble],["mid",1+a.mid],["quiet",1+a.quiet],["loud",1+a.loud]]}findGenreKey(e){const t=e.toLowerCase(),a=Object.keys(Ae.adjectives).filter(i=>i!=="default"&&t.includes(i));return a.length>0?a[0]:"default"}}class ye{static select(e,t,a,i="relative"){if(e.length===0)throw new Error("Cannot select from empty items array");const n=this.getFinalWeights(e,t,i),r=e.map(o=>{const l=this.getItemKey(o),c=l in n?n[l]:1;return[o,c]});return a.weightedChoice(r)}static selectMultiple(e,t,a,i,n="relative"){if(e.length===0)return[];if(i<=0)return[];if(i>=e.length)return a.shuffle(e);const r=[],o=[...e],l={...t};for(let c=0;c<i&&o.length>0;c++){const u=this.select(o,l,a,n);r.push(u);const d=o.indexOf(u);if(d!==-1){const h=this.getItemKey(u);o.splice(d,1),delete l[h]}}return r}static getProbabilities(e,t,a="relative"){if(e.length===0)return{};const i=this.getFinalWeights(e,t,a),n=Object.values(i).reduce((o,l)=>o+l,0);if(n===0){const o={};for(const l of e){const c=this.getItemKey(l);o[c]=1/e.length}return o}const r={};for(const o of e){const l=this.getItemKey(o),c=i[l]||0;r[l]=n>0?c/n:0}return r}static normalizeWeights(e,t,a="relative"){if(e.length===0)return{};const i=ye.getFinalWeights(e,t,a),n=Object.values(i).reduce((o,l)=>o+l,0);if(n===0){const o={};for(const l of e){const c=ye.getItemKey(l);o[c]=1/e.length}return o}const r={};for(const o of e){const l=ye.getItemKey(o);r[l]=i[l]/n}return r}static getItemKey(e){return typeof e=="string"?e:e&&typeof e=="object"&&"name"in e?String(e.name):String(e)}static getFinalWeights(e,t,a){const i={};if(a==="default")for(const n of e){const r=this.getItemKey(n);i[r]=1}else if(a==="absolute")for(const n of e){const r=this.getItemKey(n);i[r]=t[r]!==void 0?t[r]:1}else for(const n of e){const r=this.getItemKey(n);i[r]=t[r]!==void 0?t[r]:1}return i}}const ga=[{id:"barbarian_rage",name:"Rage",description:"In battle, you fight with primal fury. On your turn, you can enter a rage as a bonus action. While raging, you gain the following benefits if you aren't wearing heavy armor: Advantage on Strength checks and Strength saving throws, +2 damage bonus with melee weapons using Strength, resistance to bludgeoning, piercing, and slashing damage.",type:"active",level:1,class:"Barbarian",effects:[{type:"stat_bonus",target:"melee_damage",value:2,condition:"while raging"}],source:"default",tags:["combat","damage"]},{id:"barbarian_unarmored_defense",name:"Unarmored Defense",description:"While you are not wearing any armor, your Armor Class equals 10 + your Dexterity modifier + your Constitution modifier.",type:"passive",level:1,class:"Barbarian",source:"default",tags:["defense"]},{id:"barbarian_reckless_attack",name:"Reckless Attack",description:"You can throw aside all concern for defense to attack with fierce desperation. When you make your first attack on your turn, you can decide to attack recklessly. Doing so gives you advantage on melee weapon attack rolls using Strength during this turn, but attack rolls against you have advantage until your next turn.",type:"active",level:2,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_danger_sense",name:"Danger Sense",description:"You gain an uncanny sense of when things nearby aren't as they should be, giving you edge when you attempt to dodge a danger or react to a hidden threat. You have advantage on Dexterity saving throws against effects that you can see, such as traps and spells.",type:"passive",level:2,class:"Barbarian",source:"default",tags:["defense","perception"]},{id:"barbarian_extra_attack",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_fast_movement",name:"Fast Movement",description:"Your speed increases by 10 feet while you aren't wearing heavy armor.",type:"passive",level:5,class:"Barbarian",effects:[{type:"passive_modifier",target:"speed",value:10,condition:"unarmored"}],source:"default",tags:["movement"]},{id:"barbarian_feral_instinct",name:"Feral Instinct",description:"Your instincts are so honed that you have advantage on initiative rolls. Additionally, if you are surprised at the start of combat and aren't incapacitated, you can act normally on your first turn.",type:"passive",level:7,class:"Barbarian",source:"default",tags:["combat","perception"]},{id:"barbarian_brutal_critical",name:"Brutal Critical",description:"When you score a critical hit with a melee weapon attack, you can roll one of the weapon's damage dice one additional time and add it to the extra damage of the critical hit.",type:"passive",level:9,class:"Barbarian",source:"default",tags:["combat","damage"]},{id:"barbarian_relentless_endurance",name:"Relentless Endurance",description:"When you are reduced to 0 hit points but not killed outright, you can drop to 1 hit point instead. You can't use this feature again until you finish a long rest.",type:"active",level:11,class:"Barbarian",source:"default",tags:["survival"]},{id:"barbarian_persistent_rage",name:"Persistent Rage",description:"Your rage can't end early due to fatigue.",type:"passive",level:11,class:"Barbarian",source:"default",tags:["combat"]},{id:"barbarian_indomitable_might",name:"Indomitable Might",description:"Your Strength score increases by 4, and your maximum for that score becomes 24.",type:"passive",level:18,class:"Barbarian",effects:[{type:"stat_bonus",target:"STR",value:4},{type:"passive_modifier",target:"STR_max",value:24}],source:"default",tags:["ability"]},{id:"barbarian_primal_champion",name:"Primal Champion",description:"Your Strength and Constitution scores increase by 4, and your maximum for those scores becomes 24.",type:"passive",level:20,class:"Barbarian",effects:[{type:"stat_bonus",target:"STR",value:4},{type:"stat_bonus",target:"CON",value:4},{type:"passive_modifier",target:"STR_max",value:24},{type:"passive_modifier",target:"CON_max",value:24}],source:"default",tags:["ability"]},{id:"bardic_inspiration",name:"Bardic Inspiration",description:"You can inspire others through stirring words or music. To do so, you use a bonus action on your turn to choose one creature other than yourself within 60 feet of you who can hear you. That creature gains one Bardic Inspiration die, a d6.",type:"resource",level:1,class:"Bard",source:"default",tags:["support","resource"]},{id:"jack_of_all_trades",name:"Jack of All Trades",description:"You can add half your proficiency bonus, rounded down, to any ability check you make that doesn't already include your proficiency bonus.",type:"passive",level:2,class:"Bard",source:"default",tags:["skill"]},{id:"song_of_rest",name:"Song of Rest",description:"You can use soothing music or oration to help revitalize your wounded allies during a short rest. If you or any friendly creatures who can hear your performance regain hit points at the end of the short rest, each of those creatures regains an extra 1d6 hit points.",type:"passive",level:2,class:"Bard",source:"default",tags:["healing","support"]},{id:"bard_college",name:"Bard College",description:"At 3rd level, you delve into the advanced techniques of a bard college of your choice. Your choice grants you features at 3rd level and again at 6th and 14th level.",type:"passive",level:3,class:"Bard",source:"default",tags:["subclass"]},{id:"bardic_versatility",name:"Bardic Versatility",description:"Whenever you reach a level in this class that grants the Ability Score Improvement feature, you can do one of the following: Replace one of your skills with another skill from the bard list, gain proficiency in one skill of your choice, gain proficiency with one musical instrument of your choice, or gain proficiency with one tool of your choice.",type:"passive",level:4,class:"Bard",source:"default",tags:["skill","proficiency"]},{id:"font_of_inspiration",name:"Font of Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain one use.",type:"passive",level:5,class:"Bard",source:"default",tags:["resource"]},{id:"countercharm",name:"Countercharm",description:"You gain the ability to use musical notes or words of power to disrupt mind-influencing effects. As an action, you can start a performance that lasts until the end of your next turn. During that time, you and any friendly creatures within 30 feet of you have advantage on saving throws against being frightened or charmed.",type:"active",level:6,class:"Bard",source:"default",tags:["support","defense"]},{id:"superior_inspiration",name:"Superior Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain two uses.",type:"passive",level:10,class:"Bard",source:"default",tags:["resource"]},{id:"magical_secrets",name:"Magical Secrets",description:"You learn two spells of your choice from any class. A spell you choose must be of a level you can cast, as shown on the Bard table, or a cantrip. The chosen spells count as bard spells for you.",type:"passive",level:10,class:"Bard",source:"default",tags:["magic"]},{id:"unforgettable_face",name:"Unforgettable Face",description:"When you speak to a creature for the first time, you can choose one creature. That creature has disadvantage on attack rolls against you for 1 minute.",type:"active",level:14,class:"Bard",source:"default",tags:["social","defense"]},{id:"magical_secrets_2",name:"Magical Secrets",description:"You learn two spells of your choice from any class.",type:"passive",level:14,class:"Bard",source:"default",tags:["magic"]},{id:"bardic_inspiration_d8",name:"Bardic Inspiration (d8)",description:"Your Bardic Inspiration die changes to a d8.",type:"passive",level:15,class:"Bard",source:"default",tags:["resource"]},{id:"magical_secrets_3",name:"Magical Secrets",description:"You learn two spells of your choice from any class.",type:"passive",level:18,class:"Bard",source:"default",tags:["magic"]},{id:"bardic_inspiration_d10",name:"Bardic Inspiration (d10)",description:"Your Bardic Inspiration die changes to a d10.",type:"passive",level:20,class:"Bard",source:"default",tags:["resource"]},{id:"superior_inspiration_3",name:"Superior Inspiration",description:"When you roll initiative and have no uses of Bardic Inspiration left, you regain three uses.",type:"passive",level:20,class:"Bard",source:"default",tags:["resource"]},{id:"divine_domain",name:"Divine Domain",description:"Choose one domain related to your deity: Knowledge, Life, Light, Nature, Tempest, Trickery, or War. Each domain grants domain spells and other features at levels 1, 2, 6, 8, and 17.",type:"passive",level:1,class:"Cleric",source:"default",tags:["subclass","magic"]},{id:"divine_intervention",name:"Divine Intervention",description:"You can call on your deity to intervene on your behalf. Your deity intervenes by causing a effect related to their portfolio. You can use this feature once between long rests.",type:"active",level:10,class:"Cleric",source:"default",tags:["magic","divine"]},{id:"destroy_undead",name:"Destroy Undead",description:"When an undead fails its saving throw against your Turn Undead feature, the creature is instantly destroyed if its challenge rating is at or below a threshold shown on the Cleric table.",type:"passive",level:5,class:"Cleric",source:"default",tags:["combat","divine"]},{id:"divine_intervention_improved",name:"Divine Intervention (Improved)",description:"You can use Divine Intervention twice between long rests.",type:"active",level:20,class:"Cleric",source:"default",tags:["magic","divine"]},{id:"druidic",name:"Druidic",description:"You know Druidic, the secret language of druids. You can speak the language and use it to leave hidden messages. You and others who know this language automatically spot such a message. Others spot the message's presence with a successful DC 15 Wisdom (Perception) check but can't decipher it without magic.",type:"passive",level:1,class:"Druid",source:"default",tags:["language","secret"]},{id:"wild_shape",name:"Wild Shape",description:"You can use your action to magically assume the shape of a beast that you have seen before. You can transform into a beast with a challenge rating as shown on the Druid table.",type:"active",level:2,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_combat",name:"Wild Shape (Combat)",description:"You can transform into a beast with a challenge rating up to 1.",type:"passive",level:2,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"druid_circle",name:"Druid Circle",description:"At 2nd level, you choose to identify with a circle of druids. Your choice grants you features at 2nd level and again at 6th, 10th, and 14th level.",type:"passive",level:2,class:"Druid",source:"default",tags:["subclass"]},{id:"wild_shape_aquatic",name:"Wild Shape (Aquatic)",description:"You can transform into a beast with a swimming speed.",type:"passive",level:4,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_cr_2",name:"Wild Shape (CR 2)",description:"You can transform into a beast with a challenge rating up to 2.",type:"passive",level:8,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"wild_shape_cr_3",name:"Wild Shape (CR 3)",description:"You can transform into a beast with a challenge rating up to 3.",type:"passive",level:8,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"timeless_body",name:"Timeless Body",description:"You age slowly, appearing no older than you did at the start of your druidic studies.",type:"passive",level:18,class:"Druid",source:"default",tags:["ability"]},{id:"beast_spells",name:"Beast Spells",description:"You can cast many of your druid spells in any shape you assume using Wild Shape.",type:"passive",level:18,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"archdruid",name:"Archdruid",description:"You can use your Wild Shape an unlimited number of times.",type:"passive",level:20,class:"Druid",source:"default",tags:["magic","shapeshifting"]},{id:"fighting_style",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Archery, Defense, Dueling, Great Weapon Fighting, Protection, Two-Weapon Fighting, or Unarmed Fighting.",type:"passive",level:1,class:"Fighter",source:"default",tags:["combat","style"]},{id:"second_wind",name:"Second Wind",description:"You have a limited well of stamina you can draw on to protect yourself. As a bonus action, you can regain hit points equal to 1d10 + your fighter level.",type:"active",level:1,class:"Fighter",source:"default",tags:["healing","combat"]},{id:"action_surge",name:"Action Surge",description:"You can push yourself beyond your normal limits for a moment. On your turn, you can take one additional action.",type:"active",level:2,class:"Fighter",source:"default",tags:["combat"]},{id:"martial_archetype",name:"Martial Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles. Your choice grants you features at 3rd level and again at 7th, 10th, 15th, and 18th level.",type:"passive",level:3,class:"Fighter",source:"default",tags:["subclass"]},{id:"fighter_extra_attack",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Fighter",source:"default",tags:["combat"]},{id:"indomitable",name:"Indomitable",description:"You can reroll a saving throw that you fail. If you do so, you must use the new roll.",type:"active",level:9,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"indomitable_2",name:"Indomitable (Twice)",description:"You can use Indomitable twice between long rests.",type:"active",level:13,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"indomitable_3",name:"Indomitable (Thrice)",description:"You can use Indomitable three times between long rests.",type:"active",level:17,class:"Fighter",source:"default",tags:["defense","combat"]},{id:"action_surge_2",name:"Action Surge (Improved)",description:"You can use Action Surge twice between short rests.",type:"passive",level:17,class:"Fighter",source:"default",tags:["combat"]},{id:"unarmored_defense_monk",name:"Unarmored Defense",description:"Beginning at 1st level, while you are wearing no armor and not wielding a shield, your AC equals 10 + your Dexterity modifier + your Wisdom modifier.",type:"passive",level:1,class:"Monk",source:"default",tags:["defense"]},{id:"martial_arts",name:"Martial Arts",description:"You gain the following benefits while you are unarmed or wielding only monk weapons and you aren't wearing armor or wielding a shield: You can use Dexterity instead of Strength for attacks and damage rolls with your unarmed strikes and monk weapons, you can roll a d4 in place of the normal damage of your monk weapon or unarmed strike, and when you use the Attack action with an unarmed strike or a monk weapon, you can make one unarmed strike as a bonus action.",type:"passive",level:1,class:"Monk",source:"default",tags:["combat"]},{id:"ki",name:"Ki",description:"Starting at 2nd level, your training allows you to harness the mystic energy of ki. Your access to this energy is represented by a number of Ki points. You have a number of Ki points equal to your monk level.",type:"resource",level:2,class:"Monk",source:"default",tags:["resource","magic"]},{id:"flurry_of_blows",name:"Flurry of Blows",description:"Immediately after you take the Attack action on your turn, you can spend 1 Ki point to make two unarmed strikes as a bonus action.",type:"active",level:2,class:"Monk",source:"default",tags:["combat"]},{id:"patient_defense",name:"Patient Defense",description:"You can spend 1 Ki point to take the Dodge action as a bonus action on your turn.",type:"active",level:2,class:"Monk",source:"default",tags:["defense","combat"]},{id:"step_of_the_wind",name:"Step of the Wind",description:"You can spend 1 Ki point to take the Disengage or Dash action as a bonus action on your turn, and your jump distance is doubled for the turn.",type:"active",level:2,class:"Monk",source:"default",tags:["movement","combat"]},{id:"monastic_tradition",name:"Monastic Tradition",description:"When you reach 3rd level, you commit yourself to a monastic tradition. Your tradition grants you features at 3rd level and again at 6th, 11th, and 17th level.",type:"passive",level:3,class:"Monk",source:"default",tags:["subclass"]},{id:"slow_fall",name:"Slow Fall",description:"At 4th level, you can use your reaction when you fall to reduce any falling damage you take by an amount equal to five times your monk level.",type:"passive",level:4,class:"Monk",source:"default",tags:["defense","movement"]},{id:"extra_attack_monk",name:"Extra Attack",description:"You can attack twice, instead of once, whenever you take the Attack action on your turn.",type:"passive",level:5,class:"Monk",source:"default",tags:["combat"]},{id:"stunning_strike",name:"Stunning Strike",description:"Starting at 5th level, you can interfere with the flow of ki in an opponent's body. When you hit another creature with a melee weapon attack, you can spend 1 Ki point to attempt a stunning strike. The target must succeed on a Constitution saving throw or be stunned until the end of your next turn.",type:"active",level:5,class:"Monk",source:"default",tags:["combat"]},{id:"ki_empowered_strikes",name:"Ki-Empowered Strikes",description:"Starting at 6th level, your unarmed strikes count as magical for the purpose of overcoming resistance and immunity to nonmagical attacks and damage.",type:"passive",level:6,class:"Monk",source:"default",tags:["combat","magic"]},{id:"stillness_of_mind",name:"Stillness of Mind",description:"At 7th level, you can use your action to end one effect on yourself that is causing you to be charmed or frightened.",type:"active",level:7,class:"Monk",source:"default",tags:["defense"]},{id:"unarmored_movement",name:"Unarmored Movement",description:"Your speed increases by 10 feet while you are not wearing armor or wielding a shield. This bonus increases when you reach certain monk levels.",type:"passive",level:2,class:"Monk",effects:[{type:"passive_modifier",target:"speed",value:10,condition:"unarmored"}],source:"default",tags:["movement"]},{id:"pure_body",name:"Purity of Body",description:"At 10th level, your mastery of the ki flowing through you makes you immune to disease and poison.",type:"passive",level:10,class:"Monk",source:"default",tags:["defense","ability"]},{id:"tongue_of_the_sun_and_moon",name:"Tongue of the Sun and Moon",description:"At 13th level, you learn to touch the ki of other minds so that you understand all spoken languages. Moreover, any creature that can understand a language can understand what you say.",type:"passive",level:13,class:"Monk",source:"default",tags:["language","social"]},{id:"empty_body",name:"Empty Body",description:"At 18th level, you can spend 4 Ki points to start becoming invisible for 1 minute. During that time, you also have resistance to all damage but force damage.",type:"active",level:18,class:"Monk",source:"default",tags:["defense","magic"]},{id:"divine_sense",name:"Divine Sense",description:"As an action, you can detect good and evil. You can sense the presence of any celestial, fiend, or undead within 60 feet of you.",type:"active",level:1,class:"Paladin",source:"default",tags:["perception","divine"]},{id:"lay_on_hands",name:"Lay on Hands",description:"Your blessed touch can heal wounds. You have a pool of healing power that replenishes when you take a long rest. With that pool, you can restore a total number of hit points equal to your paladin level × 5.",type:"active",level:2,class:"Paladin",source:"default",tags:["healing","divine"]},{id:"fighting_style_paladin",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Defense, Dueling, Great Weapon Fighting, Protection, or Two-Weapon Fighting.",type:"passive",level:2,class:"Paladin",source:"default",tags:["combat","style"]},{id:"divine_smite",name:"Divine Smite",description:"When you hit a creature with a melee weapon attack, you can expend one spell slot to deal radiant damage to the target.",type:"active",level:2,class:"Paladin",source:"default",tags:["combat","divine"]},{id:"sacred_oath",name:"Sacred Oath",description:"When you reach 3rd level, you swear the oath that binds you as a paladin forever. Your choice grants you features at 3rd level and again at 7th, 15th, and 20th level.",type:"passive",level:3,class:"Paladin",source:"default",tags:["subclass","divine"]},{id:"aura_of_protection",name:"Aura of Protection",description:"Starting at 6th level, you and friendly creatures within 10 feet of you gain a bonus to all saving throws equal to your Charisma modifier (minimum of +1).",type:"passive",level:6,class:"Paladin",source:"default",tags:["defense","aura"]},{id:"aura_of_courage",name:"Aura of Courage",description:"Starting at 10th level, you and friendly creatures within 10 feet of you can't be frightened.",type:"passive",level:10,class:"Paladin",source:"default",tags:["defense","aura"]},{id:"improved_divine_smite",name:"Improved Divine Smite",description:"Starting at 11th level, you are so suffused with righteous might that all your melee weapon strikes carry divine power with them. Whenever you hit a creature with a melee weapon, the creature takes an extra 1d8 radiant damage.",type:"passive",level:11,class:"Paladin",source:"default",tags:["combat","divine"]},{id:"cleansing_touch",name:"Cleansing Touch",description:"At 14th level, you can use your action to end one spell or effect on yourself or one willing creature that you touch.",type:"active",level:14,class:"Paladin",source:"default",tags:["defense","divine"]},{id:"aura_improvements",name:"Aura Improvements",description:"At 18th level, the range of your auras increases to 30 feet.",type:"passive",level:18,class:"Paladin",source:"default",tags:["aura"]},{id:"favored_enemy",name:"Favored Enemy",description:"You have significant experience studying, tracking, hunting, and even talking to a certain type of enemy. Choose a type of favored enemy: aberrations, beasts, celestials, constructs, dragons, elementals, fey, fiends, giants, monstrosities, oozes, plants, or undead. You gain a bonus to damage rolls against creatures of the chosen type.",type:"passive",level:1,class:"Ranger",source:"default",tags:["combat"]},{id:"natural_explorer",name:"Natural Explorer",description:"You are a master of navigating the natural world. You choose a type of favored terrain: arctic, coast, desert, forest, grassland, mountain, swamp, or the Underdark. While traveling for an hour or more in your favored terrain, you gain the following benefits: Difficult terrain doesn't slow your group's travel, your group can't become lost except by magical means, and you remain alert to danger even when you are engaged in another activity.",type:"passive",level:1,class:"Ranger",source:"default",tags:["exploration"]},{id:"fighting_style_ranger",name:"Fighting Style",description:"You adopt a style of fighting as your specialty. Choose one of the following: Archery, Defense, Dueling, Great Weapon Fighting, Two-Weapon Fighting, or Mariner (if you have a swimming speed).",type:"passive",level:2,class:"Ranger",source:"default",tags:["combat","style"]},{id:"spellcasting_ranger",name:"Spellcasting",description:"By the time you reach 2nd level, you have learned to use the magical essence of nature to cast spells, much as a druid does.",type:"passive",level:2,class:"Ranger",source:"default",tags:["magic"]},{id:"ranger_archetype",name:"Ranger Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles and techniques. Your choice grants you features at 3rd level and again at 5th, 7th, 11th, and 15th level.",type:"passive",level:3,class:"Ranger",source:"default",tags:["subclass"]},{id:"prime_awareness",name:"Prime-Aware Explorer",description:"At 6th level, your mastery of the wild allows you to navigate with ease. You can use the Hide action as a bonus action. You also gain an additional Favored Enemy and Favored Terrain.",type:"passive",level:6,class:"Ranger",source:"default",tags:["exploration","stealth"]},{id:"fleet_of_foot",name:"Fleet of Foot",description:"Your speed increases by 5 feet.",type:"passive",level:8,class:"Ranger",effects:[{type:"passive_modifier",target:"speed",value:5}],source:"default",tags:["movement"]},{id:"hide_in_plain_sight",name:"Hide in Plain Sight",description:"Starting at 10th level, you can remain perfectly still for long periods to blend in with your surroundings. You can spend 1 minute creating camouflage and gain advantage on Stealth checks.",type:"active",level:10,class:"Ranger",source:"default",tags:["stealth"]},{id:"vanish",name:"Vanish",description:"Starting at 14th level, you can use the Hide action as a bonus action on your turn. Also, you can't be tracked by nonmagical means.",type:"passive",level:14,class:"Ranger",source:"default",tags:["stealth"]},{id:"feral_senses",name:"Feral Senses",description:"At 18th level, you gain preternatural senses that help you fight creatures you can't see. When you attack a creature you can't see, your inability to see it doesn't impose disadvantage on your attack rolls. You are also aware of the location of any invisible creature within 30 feet of you.",type:"passive",level:18,class:"Ranger",source:"default",tags:["perception","combat"]},{id:"sneak_attack",name:"Sneak Attack",description:"You know how to strike subtly and exploit a foe's distraction. Once per turn, you can deal an extra 1d6 damage to one creature you hit with an attack if you have advantage on the attack roll.",type:"passive",level:1,class:"Rogue",source:"default",tags:["combat","damage"]},{id:"thieves_cant",name:"Thieves' Cant",description:"During your rogue training you learned thieves' cant, a secret mix of dialect, hand signs, and body language that allows you to hide messages in seemingly normal conversation.",type:"passive",level:1,class:"Rogue",source:"default",tags:["language","secret"]},{id:"cunning_action",name:"Cunning Action",description:"You can take a bonus action on each of your turns in combat. This action can be used only to take the Dash, Disengage, or Hide action.",type:"active",level:2,class:"Rogue",source:"default",tags:["combat","movement"]},{id:"roguish_archetype",name:"Roguish Archetype",description:"At 3rd level, you choose an archetype that you strive to emulate in your combat styles and techniques. Your choice grants you features at 3rd level and then again at 9th, 13th, and 17th level.",type:"passive",level:3,class:"Rogue",source:"default",tags:["subclass"]},{id:"ability_score_improvement_rogue",name:"Ability Score Improvement",description:"At 4th level, you can increase one ability score by 2 or two ability scores by 1.",type:"passive",level:4,class:"Rogue",source:"default",tags:["ability"]},{id:"uncanny_dodge",name:"Uncanny Dodge",description:"Starting at 5th level, when an attacker that you can see hits you with an attack, you can use your reaction to halve the attack's damage against you.",type:"active",level:5,class:"Rogue",source:"default",tags:["defense","combat"]},{id:"evasion",name:"Evasion",description:"Starting at 7th level, you can nimbly dodge out of the way of certain area effects. When you are subjected to an effect that allows you to make a Dexterity saving throw to take only half damage, you instead take no damage if you succeed and only half damage if you fail.",type:"passive",level:7,class:"Rogue",source:"default",tags:["defense"]},{id:"slippery_mind",name:"Slippery Mind",description:"At 11th level, you have acquired greater mental strength. You gain proficiency in Wisdom saving throws.",type:"passive",level:11,class:"Rogue",source:"default",tags:["defense"]},{id:"elusive",name:"Elusive",description:"Beginning at 18th level, you are so evasive that attackers rarely gain the upper hand against you. No attack roll has advantage against you while you aren't incapacitated.",type:"passive",level:18,class:"Rogue",source:"default",tags:["defense"]},{id:"stroke_of_luck",name:"Stroke of Luck",description:"At 20th level, you have an uncanny knack for succeeding when you need to. If your attack misses a target within range, you can turn the miss into a hit. Alternatively, if you fail an ability check, you can treat the d20 roll as a 20.",type:"active",level:20,class:"Rogue",source:"default",tags:["combat","luck"]},{id:"sorcerous_restoration",name:"Sorcerous Restoration",description:"You can regain 4 sorcery points on a short rest.",type:"passive",level:20,class:"Sorcerer",source:"default",tags:["resource"]},{id:"font_of_magic",name:"Font of Magic",description:"You can manipulate the weave of magic to your advantage. You have 2 sorcery points. You gain more as you level. You can use sorcery points to create additional spell slots or gain spell slots.",type:"resource",level:2,class:"Sorcerer",source:"default",tags:["resource","magic"]},{id:"metamagic",name:"Metamagic",description:"At 3rd level, you gain the ability to twist your spells to suit your needs. You gain two metamagic options of your choice: Careful Spell, Distant Spell, Empowered Spell, Extended Spell, Heightened Spell, Quickened Spell, Subtle Spell, Twinned Spell.",type:"active",level:3,class:"Sorcerer",source:"default",tags:["magic"]},{id:"sorcerous_origin",name:"Sorcerous Origin",description:"Choose a sorcerous origin, which describes the source of your magical power: Draconic Bloodline, Wild Magic, or another origin.",type:"passive",level:1,class:"Sorcerer",source:"default",tags:["subclass"]},{id:"metamagic_3",name:"Metamagic (3 options)",description:"At 10th level, you gain a third metamagic option.",type:"active",level:10,class:"Sorcerer",source:"default",tags:["magic"]},{id:"metamagic_4",name:"Metamagic (4 options)",description:"At 17th level, you gain a fourth metamagic option.",type:"active",level:17,class:"Sorcerer",source:"default",tags:["magic"]},{id:"otherworldly_patron",name:"Otherworldly Patron",description:"At 1st level, you have struck a bargain with an otherworldly being of power. Your choice grants you features at 1st level and again at 6th, 10th, and 14th level.",type:"passive",level:1,class:"Warlock",source:"default",tags:["subclass"]},{id:"pact_magic",name:"Pact Magic",description:"You have learned to delve into the secrets of magic to harness the power of your patron. You know two cantrips and a number of spells.",type:"passive",level:1,class:"Warlock",source:"default",tags:["magic"]},{id:"eldritch_master",name:"Eldritch Master",description:"At 20th level, you can draw on your inner reserve of magical power while entreating your patron to regain expended spell slots. You can regain all your warlock spell slots on a short or long rest.",type:"active",level:20,class:"Warlock",source:"default",tags:["magic","resource"]},{id:"mystic_arcanum",name:"Mystic Arcanum",description:"At 11th level, your patron bestows upon you the ability to cast a 6th-level spell. You gain additional spells at higher levels.",type:"passive",level:11,class:"Warlock",source:"default",tags:["magic"]},{id:"arcane_recovery",name:"Arcane Recovery",description:"You have learned to regain some of your magical energy by studying your spellbook. Once per day when you finish a short rest, you can recover expended spell slots whose combined level equals no more than half your wizard level.",type:"active",level:1,class:"Wizard",source:"default",tags:["magic","resource"]},{id:"arcane_tradition",name:"Arcane Tradition",description:"When you reach 2nd level, you choose an arcane tradition that shapes your practice of magic. Your choice grants you features at 2nd level and again at 6th, 10th, and 14th level.",type:"passive",level:2,class:"Wizard",source:"default",tags:["subclass"]},{id:"spell_mastery",name:"Spell Mastery",description:"At 18th level, you have achieved profound mastery over spells. Choose two 1st-level wizard spells that you know. You can cast these spells at their lowest level without expending a spell slot.",type:"passive",level:18,class:"Wizard",source:"default",tags:["magic"]},{id:"signature_spells",name:"Signature Spells",description:"When you reach 20th level, you gain mastery over two powerful spells. Choose two 3rd-level wizard spells that you know. You can cast these spells at their lowest level without expending a spell slot.",type:"passive",level:20,class:"Wizard",source:"default",tags:["magic"]}],fa=[{id:"human_versatile",name:"Versatile",description:"Humans gain proficiency in one skill of their choice.",race:"Human",source:"default",tags:["skill"]},{id:"human_extra_language",name:"Extra Language",description:"Humans can speak, read, and write one extra language of their choice.",race:"Human",source:"default",tags:["language"]},{id:"elf_darkvision",name:"Darkvision",description:"Accustomed to twilit forests and the night sky, elves have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Elf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"elf_keen_senses",name:"Keen Senses",description:"Elves have proficiency in the Perception skill.",race:"Elf",source:"default",effects:[{type:"skill_proficiency",target:"perception",value:"proficient"}],tags:["skill","perception"]},{id:"elf_fey_ancestry",name:"Fey Ancestry",description:"Elves have advantage on saving throws against being charmed, and magic can't put them to sleep.",race:"Elf",source:"default",tags:["defense","magic"]},{id:"elf_trance",name:"Trance",description:"Elves don't need to sleep. Instead, they meditate deeply, remaining semiconscious, for 4 hours a day. After resting in this way, you gain the same benefit that a human does from 8 hours of sleep.",race:"Elf",source:"default",tags:["ability"]},{id:"dwarf_darkvision",name:"Darkvision",description:"Accustomed to life underground, dwarves have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Dwarf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"dwarf_dwarven_resilience",name:"Dwarven Resilience",description:"Dwarves have advantage on saving throws against poison, and resistance against poison damage.",race:"Dwarf",source:"default",tags:["defense"]},{id:"dwarf_stonecunning",name:"Stonecunning",description:"Whenever you make an Intelligence (History) check related to the origin of stonework, you are considered proficient in the History skill and add double your proficiency bonus to the check.",race:"Dwarf",source:"default",tags:["skill","exploration"]},{id:"halfling_lucky",name:"Lucky",description:"When you roll a 1 on an attack roll, ability check, or saving throw, you can reroll the die and must use the new roll.",race:"Halfling",source:"default",tags:["luck"]},{id:"halfling_brave",name:"Brave",description:"Halflings have advantage on saving throws against being frightened.",race:"Halfling",source:"default",tags:["defense"]},{id:"halfling_halfling_nimbleness",name:"Halfling Nimbleness",description:"Halflings can move through the space of any creature that is larger than them.",race:"Halfling",source:"default",tags:["movement"]},{id:"dragonborn_draconic_ancestry",name:"Draconic Ancestry",description:"Dragonborn have draconic ancestry linked to a specific type of dragon. This determines their breath weapon and damage resistance.",race:"Dragonborn",source:"default",tags:["ancestry","combat"]},{id:"dragonborn_breath_weapon",name:"Breath Weapon",description:"Dragonborn can use their action to exhale destructive energy. Their draconic ancestry determines the size, shape, and damage type of the breath weapon.",race:"Dragonborn",source:"default",tags:["combat"]},{id:"dragonborn_damage_resistance",name:"Damage Resistance",description:"Dragonborn have resistance to the damage type associated with their draconic ancestry.",race:"Dragonborn",source:"default",tags:["defense"]},{id:"gnome_darkvision",name:"Darkvision",description:"Accustomed to life underground, gnomes have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Gnome",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"gnome_gnome_cunning",name:"Gnome Cunning",description:"Gnomes have advantage on all Intelligence, Wisdom, and Charisma saving throws against magic.",race:"Gnome",source:"default",tags:["defense","magic"]},{id:"half_elf_darkvision",name:"Darkvision",description:"Thanks to your elf blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Half-Elf",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"half_elf_fey_ancestry",name:"Fey Ancestry",description:"Half-elves have advantage on saving throws against being charmed, and magic can't put them to sleep.",race:"Half-Elf",source:"default",tags:["defense","magic"]},{id:"half_elf_skill_versatility",name:"Skill Versatility",description:"Half-elves gain proficiency in two skills of their choice.",race:"Half-Elf",source:"default",tags:["skill"]},{id:"half_orc_darkvision",name:"Darkvision",description:"Thanks to your orc blood, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Half-Orc",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"half_orc_relentless_endurance",name:"Relentless Endurance",description:"When you are reduced to 0 hit points but not killed outright, you can drop to 1 hit point instead. You can't use this feature again until you finish a long rest.",race:"Half-Orc",source:"default",tags:["survival"]},{id:"half_orc_savage_attacks",name:"Savage Attacks",description:"When you score a critical hit with a melee weapon attack, you can roll one of the weapon's damage dice one additional time and add it to the extra damage of the critical hit.",race:"Half-Orc",source:"default",tags:["combat","damage"]},{id:"tiefling_darkvision",name:"Darkvision",description:"Thanks to your infernal heritage, you have superior vision in dark and dim conditions. You can see in dim light within 60 feet of you as if it were bright light, and in darkness as if it were dim light. You can't discern color in darkness, only shades of gray.",race:"Tiefling",source:"default",effects:[{type:"ability_unlock",target:"darkvision",value:60}],tags:["vision"]},{id:"tiefling_hellish_resistance",name:"Hellish Resistance",description:"Tieflings have resistance to fire damage.",race:"Tiefling",source:"default",tags:["defense"]},{id:"tiefling_infernal_legacy",name:"Infernal Legacy",description:"Tieflings know the thaumaturgy cantrip. When you reach 3rd level, you can cast the hellish rebuke spell as a 2nd-level spell once with this trait and regain the ability to do so when you finish a long rest. When you reach 5th level, you can cast the darkness spell once with this trait and regain the ability to do so when you finish a long rest. Charisma is your spellcasting ability for these spells.",race:"Tiefling",source:"default",tags:["magic","spell"]}],ya=[{id:"athletics",name:"Athletics",ability:"STR",armorPenalty:!0,categories:["physical","exploration","combat"],source:"default",description:"Covers climbing, jumping, swimming, and other physical feats of strength. Use this skill when you need to scale walls, leap across chasms, swim in turbulent waters, or perform any other physical activity that raw strength can accomplish."},{id:"acrobatics",name:"Acrobatics",ability:"DEX",armorPenalty:!0,categories:["physical","exploration"],source:"default",description:"Covers balancing, tumbling, and maintaining footing in difficult situations. Use this skill to walk on tightropes, stay on your feet during earthquakes, perform gymnastic feats, and dodge falling objects or area effects."},{id:"sleight_of_hand",name:"Sleight of Hand",ability:"DEX",armorPenalty:!0,categories:["physical","criminal","subterfuge"],source:"default",description:"Covers manual dexterity and fine motor skills such as picking pockets, palming items, and performing magic tricks. Use this skill when you need to steal something unnoticed, hide an object on your person, or manipulate small objects with precision."},{id:"stealth",name:"Stealth",ability:"DEX",armorPenalty:!0,categories:["physical","subterfuge","exploration"],source:"default",description:"Covers hiding, moving silently, and avoiding detection. Use this skill when you want to sneak past guards, hide in shadows, stalk prey, or conceal yourself from enemies. Your environment matters—darkness and cover provide advantage."},{id:"arcana",name:"Arcana",ability:"INT",armorPenalty:!1,categories:["knowledge","magic"],source:"default",description:"Covers knowledge about magical theory, arcane traditions, magical creatures, and the planes. Use this skill to identify magical items, understand spells, recognize arcane symbols, and recall lore about wizards, dragons, and other magical phenomena."},{id:"history",name:"History",ability:"INT",armorPenalty:!1,categories:["knowledge"],source:"default",description:"Covers knowledge about historical events, legendary figures, ancient civilizations, and past conflicts. Use this skill to recall the origins of ruins, identify royal lineages, understand the significance of ancient documents, and recognize historical artifacts."},{id:"investigation",name:"Investigation",ability:"INT",armorPenalty:!1,categories:["knowledge","exploration"],source:"default",description:"Covers deductive reasoning, analyzing clues, and piecing together information. Use this skill when you need to deduce the composition of an object, follow tracks, interpret coded messages, or draw conclusions from scattered evidence."},{id:"nature",name:"Nature",ability:"INT",armorPenalty:!1,categories:["knowledge","environmental"],source:"default",description:"Covers knowledge about plants, animals, weather, and the natural world. Use this skill to identify plants and beasts, predict weather changes, understand terrain, and recall lore about the wilderness and its inhabitants."},{id:"religion",name:"Religion",ability:"INT",armorPenalty:!1,categories:["knowledge"],source:"default",description:"Covers knowledge about deities, religious traditions, divine magic, and the planes of existence. Use this skill to recognize religious symbols, understand divine spells, identify outsiders, and recall lore about gods, angels, and religious cults."},{id:"animal_handling",name:"Animal Handling",ability:"WIS",armorPenalty:!1,categories:["environmental","social"],source:"default",description:"Covers calming, training, and understanding animals. Use this skill when you need to control a mount, calm a frightened beast, read an animal's mood, or predict how an animal might react. This skill doesn't work on creatures with Intelligence higher than 2."},{id:"insight",name:"Insight",ability:"WIS",armorPenalty:!1,categories:["social","perception"],source:"default",description:"Covers reading people, detecting lies, and understanding motives. Use this skill to determine when someone is lying to you, sense hidden agendas, gauge someone's true feelings, or predict how someone might act in a social situation."},{id:"medicine",name:"Medicine",ability:"WIS",armorPenalty:!1,categories:["knowledge","practical"],source:"default",description:"Covers diagnosing illnesses, treating wounds, and stabilizing dying creatures. Use this skill to determine the cause of a strange illness, assess the severity of injuries, provide first aid, or stabilize a dying companion. This skill does not cover magical healing."},{id:"perception",name:"Perception",ability:"WIS",armorPenalty:!1,categories:["perception","exploration"],source:"default",description:"Covers awareness of your surroundings and noticing hidden things. Use this skill to spot hidden enemies, find secret doors, hear approaching creatures, detect ambushes, or notice unusual details in your environment. This is the most commonly used skill in the game."},{id:"survival",name:"Survival",ability:"WIS",armorPenalty:!1,categories:["environmental","exploration"],source:"default",description:"Covers following tracks, hunting, foraging, and navigating in the wilderness. Use this skill to follow creature tracks, find food and water, predict weather, navigate without a map, or avoid natural hazards. This skill is essential for wilderness adventures."},{id:"deception",name:"Deception",ability:"CHA",armorPenalty:!1,categories:["social","subterfuge"],source:"default",description:"Covers lying, misdirection, and hiding the truth. Use this skill when you need to convincingly tell a falsehood, maintain a cover identity, pass hidden messages, or trick someone into believing something that isn't true. Success depends on plausibility and your target's suspicions."},{id:"intimidation",name:"Intimidation",ability:"CHA",armorPenalty:!1,categories:["social","combat"],source:"default",description:"Covers frightening others and forcing compliance through threats. Use this skill when you want to scare someone into backing down, extract information through fear, force a prisoner to talk, or make an opponent hesitate. Physical presence and reputation can provide advantage."},{id:"performance",name:"Performance",ability:"CHA",armorPenalty:!1,categories:["social","entertainment"],source:"default",description:"Covers entertaining audiences through music, acting, storytelling, or other arts. Use this skill when you need to captivate an audience, earn money through performance, distract guards with entertainment, or pass as a traveling performer. Quality and audience receptiveness affect the DC."},{id:"persuasion",name:"Persuasion",ability:"CHA",armorPenalty:!1,categories:["social"],source:"default",description:"Covers influencing others through reason, charm, and good faith. Use this skill when you need to convince someone to see your point of view, negotiate favorable terms, get someone to do you a favor, or rally people to your cause. This skill works best when you're being honest."}],ch=["physical","exploration","combat","criminal","subterfuge","knowledge","magic","environmental","social","perception","practical","entertainment"],uh={"appearance.bodyTypes":["slender","athletic","muscular","stocky"],"appearance.skinTones":["#F5E6D3","#E8C4A0","#D4A574","#C68642","#8D5524","#5C3317"],"appearance.hairColors":["#1C1C1C","#3B2414","#6A4E23","#A67B5B","#D4AF37","#E9C2A6","#B55239","#DC143C","#C0C0C0","#FFFFFF"],"appearance.hairStyles":["short","long","bald","braided","curly","wavy","straight","ponytail","mohawk","dreadlocks"],"appearance.eyeColors":["#3B2414","#6F4E37","#228B22","#4169E1","#708090","#000000"],"appearance.facialFeatures":["scar on cheek","tattoo on forehead","piercing","freckles","beard","mustache","clean-shaven","birthmark","sharp jawline","soft features"]};function Wa(){const s=R.getInstance();for(const[e,t]of Object.entries(uh))s.initializeDefaults(e,[...t])}function _n(){return R.getInstance().getRegisteredCategories().some(t=>String(t).startsWith("appearance."))}function za(){_n()||Wa()}const dh=Object.values(Ia),hh=Object.entries(Rt).map(([s,e])=>({class:s,cantrips:e.cantrips,spells_by_level:e.spells_by_level}));function qa(){const s=R.getInstance();s.initializeDefaults("spells",dh);for(const e of hh){const t=`spells.${e.class}`;s.initializeDefaults(t,[e])}}function An(){return R.getInstance().getRegisteredCategories().some(t=>String(t).startsWith("spells"))}function Ot(){An()||qa()}const ph=Object.values(Ze).map(s=>({...s,source:"default",spawnWeight:s.spawnWeight??1,tags:s.tags??[]}));function Ua(){R.getInstance().initializeDefaults("equipment",ph)}function kn(){return R.getInstance().getRegisteredCategories().some(t=>t==="equipment")}function Ga(){kn()||Ua()}function ja(){R.getInstance().initializeDefaults("races",[...Ps])}function Tn(){return R.getInstance().getRegisteredCategories().some(t=>t==="races")}function Va(){Tn()||ja()}function Ya(){R.getInstance().initializeDefaults("races.data",[...$s])}function Cn(){return R.getInstance().getRegisteredCategories().some(t=>t==="races.data")}function In(){Cn()||Ya()}function Za(){R.getInstance().initializeDefaults("classes",[...Ds])}function En(){return R.getInstance().getRegisteredCategories().some(t=>t==="classes")}function Xa(){En()||Za()}function mh(){Wa(),qa(),Ua(),ja(),Ya(),Za(),Ka(),Qa()}function gh(){za(),Ot(),Ga(),Va(),In(),Xa(),Ja(),Rn()}function Ka(){const s=R.getInstance(),e={};for(const a of ga)e[a.class]||(e[a.class]=[]),e[a.class].push(a);s.initializeDefaults("classFeatures",[...ga]);for(const[a,i]of Object.entries(e)){const n=`classFeatures.${a}`;s.initializeDefaults(n,i)}const t={};for(const a of fa)t[a.race]||(t[a.race]=[]),t[a.race].push(a);s.initializeDefaults("racialTraits",[...fa]);for(const[a,i]of Object.entries(t)){const n=`racialTraits.${a}`;s.initializeDefaults(n,i)}}function Mn(){return R.getInstance().getRegisteredCategories().some(t=>t==="classFeatures"||t==="racialTraits")}function Ja(){Mn()||Ka()}function Qa(){const s=R.getInstance(),e={STR:[],DEX:[],CON:[],INT:[],WIS:[],CHA:[]};for(const t of ya)e[t.ability].push(t);s.initializeDefaults("skills",[...ya]);for(const[t,a]of Object.entries(e)){const i=`skills.${t}`;s.initializeDefaults(i,a)}}function Dn(){const s=R.getInstance();return s.hasCustomData("skills")||s.getDefaults("skills").length>0}function Rn(){Dn()||Qa()}class Pn{static select(e){Va();const t=R.getInstance(),a=t.get("races"),i=t.getWeights("races"),n=t.getMode("races")||"default";return ye.select(a,i,e,n)}}const ci=class ci{static suggest(e,t){Xa();const a=R.getInstance(),i=a.get("classes"),n=this.calculateAllAffinities(e,i),r=this.calculateProbabilities(n),o=a.getWeights("classes"),l=this.applyCustomWeights(r,o,i),c=Object.entries(l).map(([u,d])=>[u,d]);return t.weightedChoice(c)}static calculateAllAffinities(e,t){const a={};for(const i of t)a[i]=this.calculateClassAffinity(e,i);return a}static calculateClassAffinity(e,t){let a=this.getCustomClassAudioPreferences(t);if(a||(a=Rs[t]),!a)return .5;let i=0;return i+=this.getTraitContribution(e,a.primary,a,1),a.secondary&&(i+=this.getTraitContribution(e,a.secondary,a,.5)),a.tertiary&&(i+=this.getTraitContribution(e,a.tertiary,a,.25)),i}static getCustomClassAudioPreferences(e){try{const a=R.getInstance().get("classes.data");if(a&&Array.isArray(a)){const i=a.find(n=>n.name===e);if(i&&i.audio_preferences)return i.audio_preferences}}catch{}}static getTraitContribution(e,t,a,i){switch(t){case"bass":return e.bass_dominance*(a.bass||1)*i;case"treble":return e.treble_dominance*(a.treble||1)*i;case"mid":return e.mid_dominance*(a.mid||1)*i;case"amplitude":return e.average_amplitude*(a.amplitude||1)*i;case"chaos":{const n=e.bass_dominance,r=e.treble_dominance,o=e.mid_dominance;return(1-(Math.max(n,r,o)-Math.min(n,r,o)))*i}default:return 0}}static calculateProbabilities(e){const t=Object.keys(e),a=t.length;if(a===0)return{};const i=Object.values(e).reduce((d,h)=>d+h,0);if(i===0){const d=1/a,h={};for(const p of t)h[p]=d;return h}const n=this.BASELINE_PROBABILITY*a,r=1-n,o=n>=1?1/a:this.BASELINE_PROBABILITY,l=n>=1?0:r,c={};for(const d of t){const h=e[d]/i;c[d]=o+h*l}const u=Object.values(c).reduce((d,h)=>d+h,0);for(const d of t)c[d]/=u;return c}static applyCustomWeights(e,t,a){if(Object.keys(t).length===0)return e;const i=Object.values(t).reduce((l,c)=>l+Math.max(0,c),0);if(i===0)return e;const n={};for(const[l,c]of Object.entries(t))a.includes(l)&&(n[l]=Math.max(0,c)/i);const r=1-Object.values(n).reduce((l,c)=>l+c,0),o=Object.entries(e).filter(([l])=>!t[l]).reduce((l,[,c])=>l+c,0);for(const l of a)!t[l]&&e[l]&&(o>0?n[l]=e[l]/o*r:n[l]=r/a.length);return n}};ci.BASELINE_PROBABILITY=.04;let Bt=ci;class Et{static getSpicedValue(e,t,a){let i;switch(e){case"bass":i=a.rms_energy??a.dynamic_range??t;break;case"mid":a.spectral_centroid!==void 0?i=Math.min(1,Math.max(0,(a.spectral_centroid-100)/9900)):i=a.dynamic_range??t;break;case"treble":a.zero_crossing_rate!==void 0?i=a.zero_crossing_rate:a.spectral_centroid!==void 0?i=Math.min(1,Math.max(0,(a.spectral_centroid-100)/9900)):i=t;break}return(t+i)/2}static createFrequencyMappings(e){const t=["STR","DEX","CON","INT","WIS","CHA"],a=new Map,i=e.shuffle(t),n=["bass","mid","treble"];for(let r=0;r<3;r++){const o=n[r],l=i[r*2],c=i[r*2+1],u=e.random()<.5;a.set(o,{abilities:[l,c],firstHasSpice:u})}return a}static calculateBaseScores(e,t){const{bass_dominance:a,mid_dominance:i,treble_dominance:n}=e,r=this.createFrequencyMappings(t),o={STR:8,DEX:8,CON:8,INT:8,WIS:8,CHA:8},l=3.5,c=3.5;for(const[u,d]of r){const[h,p]=d.abilities,g=d.firstHasSpice;let m;switch(u){case"bass":m=a;break;case"mid":m=i;break;case"treble":m=n;break}const f=g?this.getSpicedValue(u,m,e):m,v=g?m:this.getSpicedValue(u,m,e),w=t.random(),E=t.random();o[h]=Math.floor(8+w*c+f*l),o[p]=Math.floor(8+E*c+v*l)}return o}static applyRacialBonuses(e,t){const a=zt(t);if(!a)return console.warn(`Unknown race: "${t}", using no ability bonuses`),{...e};const i=a.ability_bonuses,n={...e};if(i)for(const[r,o]of Object.entries(i)){const l=r;n[l]=Math.min(20,(n[l]||0)+(o||0))}return n}static calculateModifiers(e){return{STR:Math.floor((e.STR-10)/2),DEX:Math.floor((e.DEX-10)/2),CON:Math.floor((e.CON-10)/2),INT:Math.floor((e.INT-10)/2),WIS:Math.floor((e.WIS-10)/2),CHA:Math.floor((e.CHA-10)/2)}}}class xn{static getSkillListDefinition(e){try{const a=R.getInstance().get("skillLists");return Array.isArray(a)?a.find(i=>i.class===e):void 0}catch{return}}static assignSkills(e,t,a){const i=pe.getInstance(),n=i.getAllSkills(),r={};for(const h of n)r[h.id]="none";const o=Ea(e);if(!o)return console.warn(`SkillAssigner: Unknown class "${e}", using default skill assignment`),r;const l=this.validateSkills(o.available_skills,i),c=a?this.filterSkillsByPrerequisites(l,i,a):l,u=this.getSkillListDefinition(e),d=this.selectSkills(c,o.skill_count,t,u?.selectionWeights);for(const h of d)r[h]="proficient";if(o.has_expertise&&o.expertise_count){const h=this.selectSkills(d,o.expertise_count,t);for(const p of h)r[p]="expertise"}return r}static validateSkills(e,t){const a=[];for(const i of e)t.isValidSkill(i)?a.push(i):console.warn(`SkillAssigner: Invalid skill ID "${i}" not found in SkillQuery. Skipping.`);return a}static filterSkillsByPrerequisites(e,t,a){const i=[];for(const n of e){const r=t.getSkill(n);r&&(r.prerequisites&&!we.validateSkillPrerequisites(r.prerequisites,a).valid||i.push(n))}return i}static selectSkills(e,t,a,i){if(!i||i.mode==="default")return this.selectSkillsEqualWeight(e,t,a);const{weights:n,mode:r}=i,o=[];for(const u of e){const d=n[u];if(d!==0)if(r==="absolute"){if(d!==void 0){const h=Math.round(d*10);for(let p=0;p<h;p++)o.push(u)}}else{const p=Math.round((d??1)*10);for(let g=0;g<p;g++)o.push(u)}}if(o.length===0)return this.selectSkillsEqualWeight(e,t,a);for(let u=o.length-1;u>0;u--){const d=Math.floor(a.random()*(u+1));[o[u],o[d]]=[o[d],o[u]]}const l=[],c=new Set;for(const u of o)if(!c.has(u)&&(l.push(u),c.add(u),l.length>=t))break;return l}static selectSkillsEqualWeight(e,t,a){const i=[...e];for(let n=i.length-1;n>0;n--){const r=Math.floor(a.random()*(n+1));[i[n],i[r]]=[i[r],i[n]]}return i.slice(0,t)}}const fh=["Wizard","Sorcerer","Warlock","Bard","Cleric","Druid","Paladin"];class Nn{static generate(e,t,a){za();const i=new Se(e),n=R.getInstance(),r=n.get("appearance.bodyTypes"),o=n.getWeights("appearance.bodyTypes"),l=n.getMode("appearance.bodyTypes")||"default",c=n.get("appearance.skinTones"),u=n.getWeights("appearance.skinTones"),d=n.getMode("appearance.skinTones")||"default",h=n.get("appearance.hairStyles"),p=n.getWeights("appearance.hairStyles"),g=n.getMode("appearance.hairStyles")||"default",m=n.get("appearance.hairColors"),f=n.getWeights("appearance.hairColors"),v=n.getMode("appearance.hairColors")||"default",w=n.get("appearance.eyeColors"),E=n.getWeights("appearance.eyeColors"),I=n.getMode("appearance.eyeColors")||"default",D=n.get("appearance.facialFeatures"),x=n.getWeights("appearance.facialFeatures"),N=n.getMode("appearance.facialFeatures")||"default",$=ye.select(r,o,i,l),H=ye.select(c,u,i,d),te=ye.select(h,p,i,g),Q=ye.select(m,f,i,v),X=ye.select(w,E,i,I),de=i.randomInt(1,4),ee=ye.selectMultiple(D,x,i,de,N),O=a.color_palette,P=O?.primary_color,L=O?.secondary_color,_e=O?.accent_color;let ge;return fh.includes(t)&&(ge=this.generateAuraColor(t,O?.primary_color)),{body_type:$,skin_tone:H,hair_style:te,hair_color:Q,eye_color:X,facial_features:ee,primary_color:P,secondary_color:L,accent_color:_e,aura_color:ge}}static generateAuraColor(e,t){return t?this.adjustColorBrightness(t,1.3):{Wizard:"#4169E1",Sorcerer:"#DC143C",Warlock:"#8B008B",Bard:"#FFD700",Cleric:"#F0E68C",Druid:"#228B22",Paladin:"#FFE4B5"}[e]||"#FFFFFF"}static adjustColorBrightness(e,t){const a=e.replace("#",""),i=parseInt(a.substring(0,2),16),n=parseInt(a.substring(2,4),16),r=parseInt(a.substring(4,6),16),o=Math.min(255,Math.floor(i*t)),l=Math.min(255,Math.floor(n*t)),c=Math.min(255,Math.floor(r*t)),u=d=>d.toString(16).padStart(2,"0");return`#${u(o)}${u(l)}${u(c)}`}}class va{static isSpellcaster(e){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(e)}static getSpellSlots(e,t){if(!this.isSpellcaster(e))return{};const a={};for(let n=0;n<=9;n++)a[n]={total:0,used:0};const i=Da(e,t);if(!i)return a;for(const[n,r]of Object.entries(i)){const o=parseInt(n,10);a[o]={total:r,used:0}}return a}static getCantrips(e){if(!this.isSpellcaster(e))return[];Ot();const t=R.getInstance(),a=la(e),i=a?[...a.cantrips]:[],n=`spells.${e}`,r=t.get(n);for(const o of r){const l=o;if(l.cantrips&&l.cantrips.length>0)for(const c of l.cantrips)i.includes(c)||i.push(c)}return i}static getKnownSpells(e,t,a){if(!this.isSpellcaster(e))return[];Ot();const i=R.getInstance(),n=la(e);if(!n||!n.spells_by_level)return[];[...n.cantrips];const r={};for(const[d,h]of Object.entries(n.spells_by_level))r[Number(d)]=[...h];const o=`spells.${e}`,l=i.get(o);for(const d of l){const h=d;if(h.cantrips&&h.cantrips.length>0)for(const p of h.cantrips);if(h.spells_by_level)for(const[p,g]of Object.entries(h.spells_by_level)){const m=Number(p);r[m]||(r[m]=[]);for(const f of g)r[m].includes(f)||r[m].push(f)}}const c={spells_by_level:r},u=[];for(let d=1;d<=t&&d<=9;d++){const h=c.spells_by_level[d];h&&h.length>0&&u.push(...h)}return a?this.filterSpellsByPrerequisites(u,a):u}static filterSpellsByPrerequisites(e,t){const a=[];for(const i of e){const n=Ia[i];if(!n){a.push(i);continue}n.prerequisites&&!De.validateSpellPrerequisites(n.prerequisites,t).valid||a.push(i)}return a}static initializeSpells(e,t,a){return{spell_slots:this.getSpellSlots(e,t),known_spells:this.getKnownSpells(e,t,a),cantrips:this.getCantrips(e)}}static getSpellCountAtLevel(e,t){return t[e]?.total||0}static useSpellSlot(e,t){if(t<1||t>9)throw new Error(`Invalid spell level: ${t}. Must be between 1 and 9.`);const a={...e},i=a[t];return i&&i.used<i.total&&(a[t]={...i,used:i.used+1}),a}static restoreSpellSlots(e,t){const a={...e};if(t!==void 0){const i=a[t];i&&(a[t]={...i,used:0})}else for(let i=1;i<=9;i++){const n=a[i];n&&(a[i]={...n,used:0})}return a}static filterCharacterSpells(e){if(!e.spells)return e;const t=this.filterSpellsByPrerequisites(e.spells.known_spells||[],e),a=this.filterSpellsByPrerequisites(e.spells.cantrips||[],e);return{...e,spells:{...e.spells,known_spells:t,cantrips:a}}}}const ba={Barbarian:{weapons:["Greataxe","Handaxe"],armor:["No Armor"],items:["Explorer's Pack","Javelin"]},Bard:{weapons:["Rapier","Dagger","Dagger"],armor:["Leather Armor"],items:["Lute","Entertainer's Pack"]},Cleric:{weapons:["Mace","Light Crossbow"],armor:["Scale Mail","Shield"],items:["Holy Symbol","Priest's Pack","Healer's Kit"]},Druid:{weapons:["Quarterstaff","Dagger"],armor:["Leather Armor","Shield"],items:["Druidic Focus","Explorer's Pack"]},Fighter:{weapons:["Longsword"],armor:["Chain Mail","Shield"],items:["Martial Melee Weapon","Bedroll","Rope"]},Monk:{weapons:["Shortsword","Martial Arts"],armor:["No Armor"],items:["Insignia","Traveler's Pack","Dart"]},Paladin:{weapons:["Longsword"],armor:["Chain Mail","Shield"],items:["Holy Symbol","Priest's Pack"]},Ranger:{weapons:["Longsword","Shortsword","Longbow","Dagger"],armor:["Leather Armor"],items:["Explorer's Pack"]},Rogue:{weapons:["Rapier","Hand Crossbow","Dagger"],armor:["Leather Armor"],items:["Burglar's Pack","Thieves' Tools"]},Sorcerer:{weapons:["Light Crossbow","Dagger"],armor:["Leather Armor"],items:["Arcane Focus","Dungeoneer's Pack"]},Warlock:{weapons:["Light Crossbow","Dagger"],armor:["Leather Armor"],items:["Arcane Focus","Scholar's Pack"]},Wizard:{weapons:["Quarterstaff","Dagger"],armor:["No Armor"],items:["Spellbook","Component Pouch","Scholar's Pack","Ink & Quill"]}};function $n(s){if(s in ba)return ba[s];try{const e=R.getInstance(),t=`classStartingEquipment.${s}`,a=e.get(t);if(a&&Array.isArray(a)&&a.length>0){const i=a[0];return{weapons:i.weapons,armor:i.armor,items:i.items}}}catch{}}function wa(s,e,t){s.ability_scores[e]+=t;const a=s.ability_scores[e];s.ability_modifiers[e]=Math.floor((a-10)/2)}function Sa(s,e,t){const a=s.skills[e];if(!a||a==="none"){s.skills[e]=t;return}t==="expertise"&&(s.skills[e]=t)}class le{static equipItem(e,t,a){const i={applied:!1,count:0,errors:[]};if(e.equipment_effects||(e.equipment_effects=[]),this.findEquipmentEffect(e,t.name,a))return i.applied=!0,i;if(t.properties){const o=this.validateRequirements(e,t,t.properties);if(o.length>0)return i.errors.push(...o),i}const r={source:t.name,instanceId:a,effects:t.properties?[...t.properties]:[],features:[],skills:[],spells:[]};if(t.properties)for(const o of t.properties)try{this.applyProperty(e,o,t.name),i.count++}catch(l){const c=l instanceof Error?l.message:String(l);i.errors.push(`Failed to apply property from ${t.name}: ${c}`)}return t.grantsFeatures&&(this.applyEquipmentFeatures(e,t,a,r.features),i.count+=r.features.length),t.grantsSkills&&(this.applyEquipmentSkills(e,t,a,r.skills),i.count+=r.skills.length),t.grantsSpells&&(r.spells=t.grantsSpells.map(o=>({spellId:o.spellId,level:o.level,uses:o.uses,recharge:o.recharge})),this.addSpellsToCharacter(e,t.grantsSpells),i.count+=r.spells.length),e.equipment_effects.push(r),i.applied=i.errors.length===0,i}static unequipItem(e,t,a){const i={applied:!1,count:0,errors:[]};if(!e.equipment_effects)return i.errors.push("No equipment effects found on character"),i;const n=e.equipment_effects.findIndex(o=>o.source===t&&o.instanceId===a);if(n===-1)return i.errors.push(`Equipment "${t}" effects not found`),i;const r=e.equipment_effects[n];return r.effects.length>0&&(this.removeProperties(e,r.effects),i.count+=r.effects.length),r.features.length>0&&(this.removeEquipmentFeatures(e,t,a),i.count+=r.features.length),r.skills.length>0&&(this.removeEquipmentSkills(e,r.skills),i.count+=r.skills.length),r.spells&&r.spells.length>0&&(this.removeSpellsFromCharacter(e,r.spells),i.count+=r.spells.length),e.equipment_effects.splice(n,1),i.applied=i.count>0,i}static reapplyEquipmentEffects(e){const t={applied:!1,count:0,errors:[]};if(!e.equipment_effects||e.equipment_effects.length===0)return t;const a=[...e.equipment_effects];e.equipment_effects=[];for(const i of a){const n={source:i.source,instanceId:i.instanceId,effects:[...i.effects],features:[...i.features],skills:[...i.skills],spells:i.spells?[...i.spells]:[]};for(const r of i.effects)try{this.applyProperty(e,r,i.source),t.count++}catch(o){const l=o instanceof Error?o.message:String(o);t.errors.push(`Failed to reapply property from ${i.source}: ${l}`)}for(const r of i.features)this.addFeatureToCharacter(e,r),t.count++;for(const r of i.skills)this.addSkillToCharacter(e,r),t.count++;i.spells&&(this.addSpellsToCharacter(e,i.spells),t.count+=i.spells.length),e.equipment_effects.push(n)}return t.applied=t.count>0,t}static getActiveEffects(e){if(!e.equipment_effects)return[];const t=[];for(const a of e.equipment_effects)t.push(...a.effects);return t}static evaluateACFormula(e,t){const a=e.trim();if(/^\d+$/.test(a))return parseInt(a,10);const i=a.match(/^(\d+)\s*\+\s*min\(DEX,\s*(\d+)\)$/i);if(i){const r=parseInt(i[1],10),o=parseInt(i[2],10);return r+Math.min(t,o)}const n=a.match(/^(\d+)\s*\+\s*DEX$/i);if(n)return parseInt(n[1],10)+t;throw new Error(`Unknown AC formula: "${e}"`)}static applyProperty(e,t,a){if(!(t.condition&&!this.checkCondition(t.condition,e)))switch(t.type){case"stat_bonus":case"skill_proficiency":case"ability_unlock":case"passive_modifier":this.applyPropertyViaFeatureEffectApplier(e,t);break;case"special_property":this.trackSpecialProperty(e,t,a);break;case"damage_bonus":this.trackDamageBonus(e,t,a);break;case"stat_requirement":this.trackStatRequirement(e,t,a);break;default:throw new Error(`Unknown property type: ${t.type}`)}}static applyPropertyViaFeatureEffectApplier(e,t){switch(t.type){case"stat_bonus":this.applyStatBonus(e,t);break;case"skill_proficiency":this.applySkillProficiency(e,t);break;case"ability_unlock":this.applyAbilityUnlock(e,t);break;case"passive_modifier":this.applyPassiveModifier(e,t);break}}static applyStatBonus(e,t){const a=t.target,i=t.value;if(Pe(a)){wa(e,a,i);return}}static applySkillProficiency(e,t){const a=t.target.toLowerCase(),i=t.value;Sa(e,a,i)}static applyAbilityUnlock(e,t){}static applyPassiveModifier(e,t){const a=t.target,i=t.value;if(a==="speed"){e.speed+=i;return}if(a==="ac"||a==="armor_class"){if(typeof i=="string"){const n=e.ability_modifiers.DEX??0;e.armor_class=le.evaluateACFormula(i,n)}else e.armor_class+=i;return}if(a==="max_hp"||a==="hp_max"){e.hp.max+=i,e.hp.current+=i;return}}static trackSpecialProperty(e,t,a){}static trackDamageBonus(e,t,a){}static removeProperties(e,t){for(let a=t.length-1;a>=0;a--){const i=t[a];switch(i.type){case"stat_bonus":this.removeStatBonus(e,i);break;case"skill_proficiency":this.removeSkillProficiency(e,i);break;case"passive_modifier":this.removePassiveModifier(e,i);break}}}static removeStatBonus(e,t){const a=t.target,i=t.value;Pe(a)&&wa(e,a,-i)}static removeSkillProficiency(e,t){const a=t.target.toLowerCase();e.skills[a]="none"}static removePassiveModifier(e,t){const a=t.target,i=t.value;a==="speed"?e.speed-=i:a==="ac"||a==="armor_class"?typeof i=="string"?e.armor_class=10+(e.ability_modifiers.DEX??0):e.armor_class-=i:(a==="max_hp"||a==="hp_max")&&(e.hp.max-=i,e.hp.current=Math.min(e.hp.current,e.hp.max))}static applyEquipmentFeatures(e,t,a,i){if(t.grantsFeatures)for(const n of t.grantsFeatures)if(typeof n=="string"){const r=re.getInstance(),o=r.getClassFeatureById(n),l=o?void 0:r.getRacialTraitById(n);if(o||l){const u={featureId:n,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};i.push(u),this.addFeatureToCharacter(e,u)}}else{const r={featureId:n.id,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};if(i.push(r),this.addFeatureToCharacter(e,r),n.effects)for(const o of n.effects)this.applyProperty(e,o,t.name)}}static applyEquipmentSkills(e,t,a,i){if(t.grantsSkills)for(const n of t.grantsSkills){const r={skillId:n.skillId,level:n.level,source:"equipment",equipmentName:t.name,instanceId:a,sourceType:t.source??"default"};i.push(r),this.addSkillToCharacter(e,r)}}static addSpellsToCharacter(e,t){e.spells||(e.spells={spell_slots:{},known_spells:[],cantrips:[]});for(const a of t)a.level===0?e.spells.cantrips.includes(a.spellId)||e.spells.cantrips.push(a.spellId):e.spells.known_spells.includes(a.spellId)||e.spells.known_spells.push(a.spellId)}static removeSpellsFromCharacter(e,t){if(e.spells)for(const a of t)if(a.level===0){const i=e.spells.cantrips.indexOf(a.spellId);i>-1&&e.spells.cantrips.splice(i,1)}else{const i=e.spells.known_spells.indexOf(a.spellId);i>-1&&e.spells.known_spells.splice(i,1)}}static addFeatureToCharacter(e,t){e.class_features.includes(t.featureId)||e.class_features.push(t.featureId)}static addSkillToCharacter(e,t){const a=t.skillId.toLowerCase();Sa(e,a,t.level)}static removeEquipmentFeatures(e,t,a){if(!e.equipment_effects)return;const i=e.equipment_effects.filter(n=>n.source===t&&n.instanceId===a).flatMap(n=>n.features);for(const n of i){const r=e.class_features.indexOf(n.featureId);r>-1&&e.class_features.splice(r,1)}}static removeEquipmentSkills(e,t){for(const a of t){const i=a.skillId.toLowerCase();e.skills[i]="none"}}static checkCondition(e,t){switch(e.type){case"while_equipped":return!0;case"on_hit":case"on_damage_taken":return!1;case"wielder_race":return t.race===e.value;case"wielder_class":return t.class===e.value;case"at_time_of_day":return!0;case"vs_creature_type":return!1;case"custom":return!0;default:return!0}}static findEquipmentEffect(e,t,a){if(e.equipment_effects)return e.equipment_effects.find(i=>i.source===t&&i.instanceId===a)}static validateRequirements(e,t,a){const i=[];for(const n of a)if(n.type==="stat_requirement"){const r=n.target,o=n.value;if(Pe(r)){const l=e.ability_scores[r];l<o&&i.push(`${t.name} requires ${r} ${o}, but character has ${r} ${l}`)}}return i}static trackStatRequirement(e,t,a){}}class dt{static ensureInitialized(){Ga()}static getEquipmentDataStatic(e){return dt.getEquipmentData(e)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static getStartingEquipment(e){this.ensureInitialized();const t=$n(e);return t?{weapons:[...t.weapons],armor:[...t.armor],items:[...t.items]}:{weapons:[],armor:[],items:[]}}static initializeEquipment(e){this.ensureInitialized();const t=this.getStartingEquipment(e),a=[],i=[],n=[],r=[...t.weapons,...t.armor,...t.items];for(const l of r){const c=this.getEquipmentData(l);if(c){let u;c.type==="weapon"?u=a:c.type==="armor"?u=i:u=n;const d=u.find(h=>h.name===l);d?d.quantity+=1:u.push({name:l,quantity:1,equipped:!1})}else{console.warn(`EquipmentGenerator: Unknown item "${l}" in starting equipment.`);const u=n.find(d=>d.name===l);u?u.quantity+=1:n.push({name:l,quantity:1,equipped:!1})}}const o=this.getAmmunitionType(e,t.weapons);if(o){const l=this.getAmmunitionQuantity(e),c=n.find(u=>u.name===o);c?c.quantity+=l:n.push({name:o,quantity:l,equipped:!1})}return a.length>0&&(a[0].equipped=!0),i.length>0&&(i[0].equipped=!0),{weapons:a,armor:i,items:n,totalWeight:this.calculateTotalWeight(a,i,n),equippedWeight:this.calculateEquippedWeight(a,i,n)}}static addItem(e,t,a=1){this.ensureInitialized();const i=this.getEquipmentData(t);if(!i)return e;const n=this.cloneEquipment(e);let r;i.type==="weapon"?r=n.weapons:i.type==="armor"?r=n.armor:r=n.items;const o=r.find(l=>l.name===t);return o?a>0&&(o.quantity+=a):r.push({name:t,quantity:Math.max(0,a),equipped:!1}),n.totalWeight=this.calculateTotalWeight(n.weapons,n.armor,n.items),n.equippedWeight=this.calculateEquippedWeight(n.weapons,n.armor,n.items),n}static removeItem(e,t,a=1){if(this.ensureInitialized(),a<=0)return e;const i=this.cloneEquipment(e);let n;const r=this.getEquipmentData(t);if(!r)return e;r.type==="weapon"?n=i.weapons:r.type==="armor"?n=i.armor:n=i.items;const o=n.findIndex(l=>l.name===t);return o!==-1&&(n[o].quantity>a?n[o].quantity-=a:n.splice(o,1)),i.totalWeight=this.calculateTotalWeight(i.weapons,i.armor,i.items),i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static equipItem(e,t,a){this.ensureInitialized();const i=this.cloneEquipment(e),n=this.getEquipmentData(t);if(!n)return e;let r;n.type==="weapon"?r=i.weapons:n.type==="armor"?r=i.armor:r=i.items;const o=r.find(l=>l.name===t);if(o&&(o.equipped=!0,a)){const l=o.instanceId;le.equipItem(a,n,l)}return i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static unequipItem(e,t,a){this.ensureInitialized();const i=this.cloneEquipment(e),n=this.getEquipmentData(t);if(!n)return e;let r;n.type==="weapon"?r=i.weapons:n.type==="armor"?r=i.armor:r=i.items;const o=r.find(l=>l.name===t);if(o&&(o.equipped=!1,a)){const l=o.instanceId;le.unequipItem(a,t,l)}return i.equippedWeight=this.calculateEquippedWeight(i.weapons,i.armor,i.items),i}static getInventoryList(e){return[...e.weapons,...e.armor,...e.items]}static getEquipmentByType(e,t){return e[t]}static calculateTotalWeight(e,t,a){let i=0;for(const n of e){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}for(const n of t){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}for(const n of a){const r=this.getEquipmentData(n.name);r&&(i+=r.weight*n.quantity)}return Math.round(i*100)/100}static calculateEquippedWeight(e,t,a){let i=0;for(const n of e)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}for(const n of t)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}for(const n of a)if(n.equipped){const r=this.getEquipmentData(n.name);r&&(i+=r.weight)}return Math.round(i*100)/100}static cloneEquipment(e){return{weapons:e.weapons.map(t=>({...t})),armor:e.armor.map(t=>({...t})),items:e.items.map(t=>({...t})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight}}static getAmmunitionType(e,t){const a=t.some(o=>o==="Longbow"),i=t.some(o=>o==="Shortbow"),n=t.some(o=>o==="Light Crossbow"),r=t.some(o=>o==="Hand Crossbow");return a||i?"Arrow":n||r?"Bolt":null}static getAmmunitionQuantity(e){return{Ranger:20,Fighter:20,Rogue:20}[e]||0}static addModification(e,t,a,i,n){this.ensureInitialized();const r=this.cloneEquipment(e),o=this.getEquipmentData(t);if(!o)return e;let l;o.type==="weapon"?l=r.weapons:o.type==="armor"?l=r.armor:l=r.items;const c=l.find(d=>d.name===t);if(!c)return e;c.modifications||(c.modifications=[]),!i&&!c.instanceId&&(c.instanceId=`${t}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const u=i||c.instanceId;return c.modifications.push(a),c.equipped&&n&&(le.unequipItem(n,t,u),this.applyCombinedEffects(n,o,a,u)),r}static removeModification(e,t,a,i){this.ensureInitialized();const n=this.cloneEquipment(e),r=this.getEquipmentData(t);if(!r)return e;let o;r.type==="weapon"?o=n.weapons:r.type==="armor"?o=n.armor:o=n.items;const l=o.find(h=>h.name===t);if(!l||!l.modifications)return e;const c=l.equipped,u=l.instanceId,d=l.modifications.findIndex(h=>h.id===a);if(d!==-1&&l.modifications.splice(d,1),c&&i&&(le.unequipItem(i,t,u),le.equipItem(i,r,u),l.modifications))for(const h of l.modifications)this.applyCombinedEffects(i,r,h,u);return n}static getActiveEffects(e,t,a){this.ensureInitialized();const i=this.getEquipmentData(t);if(!i)return[];let n;i.type==="weapon"?n=e.weapons:i.type==="armor"?n=e.armor:n=e.items;const r=n.find(l=>l.name===t&&(!a||l.instanceId===a));if(!r)return[];const o=i.properties?[...i.properties]:[];if(r.modifications)for(const l of r.modifications)l.properties&&o.push(...l.properties);return o}static applyCombinedEffects(e,t,a,i){const n={...t,properties:[...t.properties||[],...a.properties],grantsFeatures:a.addsFeatures,grantsSkills:a.addsSkills,grantsSpells:a.addsSpells};le.equipItem(e,n,i)}}class ht{static applyFeatureEffects(e,t){const a={applied:!1,count:0,errors:[]};if(!t.effects||t.effects.length===0)return a;for(const i of t.effects)try{this.applySingleEffect(e,i,t.id),a.applied=!0,a.count++}catch(n){const r=n instanceof Error?n.message:String(n);a.errors.push(`Failed to apply effect from ${t.id}: ${r}`)}return a}static applyMultipleEffects(e,t){const a={applied:!1,count:0,errors:[]};for(const i of t){const n=this.applyFeatureEffects(e,i);a.applied=a.applied||n.applied,a.count+=n.count,a.errors.push(...n.errors)}return a}static applySingleEffect(e,t,a){switch(t.type){case"stat_bonus":this.applyStatBonus(e,t);break;case"skill_proficiency":this.applySkillProficiency(e,t);break;case"ability_unlock":this.applyAbilityUnlock(e,t);break;case"passive_modifier":this.applyPassiveModifier(e,t);break;case"resource_grant":this.applyResourceGrant(e,t);break;case"spell_slot_bonus":this.applySpellSlotBonus(e,t);break;default:throw new Error(`Unknown effect type: ${t.type}`)}}static applyStatBonus(e,t){const a=t.target,i=t.value;if(Pe(a)){wa(e,a,i);return}e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"stat_bonus",target:t.target,value:t.value,condition:t.condition})}static applySkillProficiency(e,t){const a=t.target.toLowerCase(),i=t.value;Sa(e,a,i)}static applyAbilityUnlock(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"ability_unlock",target:t.target,value:t.value,condition:t.condition})}static applyPassiveModifier(e,t){const a=t.target,i=t.value;if(a==="speed"){e.speed+=i;return}if(a.endsWith("_max")){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"passive_modifier",target:t.target,value:t.value,condition:t.condition});return}e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"passive_modifier",target:t.target,value:t.value,condition:t.condition})}static applyResourceGrant(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"resource_grant",target:t.target,value:t.value,condition:t.condition})}static applySpellSlotBonus(e,t){e.feature_effects||(e.feature_effects=[]),e.feature_effects.push({type:"spell_slot_bonus",target:t.target,value:t.value,condition:t.condition})}}class Vt{static registerExtensions(e){const t=R.getInstance();if(e.spells&&e.spells.length>0&&t.register("spells",e.spells),e.equipment&&e.equipment.length>0&&t.register("equipment",e.equipment),e.races&&e.races.length>0&&t.register("races",e.races),e.classes&&e.classes.length>0&&t.register("classes",e.classes),e.appearance){const{appearance:a}=e;a.bodyTypes&&a.bodyTypes.length>0&&t.register("appearance.bodyTypes",a.bodyTypes),a.skinTones&&a.skinTones.length>0&&t.register("appearance.skinTones",a.skinTones),a.hairColors&&a.hairColors.length>0&&t.register("appearance.hairColors",a.hairColors),a.hairStyles&&a.hairStyles.length>0&&t.register("appearance.hairStyles",a.hairStyles),a.eyeColors&&a.eyeColors.length>0&&t.register("appearance.eyeColors",a.eyeColors),a.facialFeatures&&a.facialFeatures.length>0&&t.register("appearance.facialFeatures",a.facialFeatures)}}static generate(e,t,a,i={}){const n=new Se(e),r=i.level||1,o=i.gameMode||"standard";Ja();const l=re.getInstance();i.extensions&&Vt.registerExtensions(i.extensions);let c;const u=i.subrace;let d;if(u==="pure")c=void 0,d=i.forceRace;else if(u!==void 0)if(i.forceRace)c=u,d=i.forceRace;else{const q=l.getRaceForSubrace(u);if(!q)throw new Error(`Cannot determine race for subrace "${u}". Either specify forceRace or ensure the subrace is registered in racial traits. Example: { forceRace: 'Elf', subrace: 'High Elf' }`);d=q,c=u}const h=d||Pn.select(n);if(c!==void 0&&u!=="pure"){const q=l.getAvailableSubraces(h);if(!q.includes(c))throw new Error(`Invalid subrace "${c}" for race "${h}". Available subraces: ${q.length>0?q.join(", "):"none"}`)}else if(c===void 0&&u===void 0){const ae=["pure",...l.getAvailableSubraces(h)],at=n.randomChoice(ae);c=at==="pure"?void 0:at}const p=i.forceClass||Bt.suggest(t,n),g=i.forceName||new Sn().generateName(e,a,t,p,i.deterministicName||!0),m=Et.calculateBaseScores(t,n),f=Et.applyRacialBonuses(m,h),v=Et.calculateModifiers(f),w=Ea(p);if(!w)throw new Error(`Unknown class: ${p}. Cannot generate character.`);const I=zt(h)?.speed??30,D=w.hit_die+v.CON,x=10+v.DEX,N=v.DEX,$=Ma[r],H=xn.assignSkills(p,n),te={STR:w.saving_throws.includes("STR"),DEX:w.saving_throws.includes("DEX"),CON:w.saving_throws.includes("CON"),INT:w.saving_throws.includes("INT"),WIS:w.saving_throws.includes("WIS"),CHA:w.saving_throws.includes("CHA")},Q=Nn.generate(e,p,t),X=l.getClassFeatures(p,r),de=c?l.getRacialTraitsForSubrace(h,c):l.getBaseRacialTraits(h),ee=dt.initializeEquipment(p),O={name:g,race:h,subrace:c,class:p,level:r,ability_scores:f,ability_modifiers:v,proficiency_bonus:$,hp:{current:D,max:D,temp:0},armor_class:x,initiative:N,speed:I,skills:H,saving_throws:te,racial_traits:[],class_features:[],appearance:Q,spells:{spell_slots:{},known_spells:[],cantrips:[]},equipment:ee,xp:{current:0,next_level:ct[r+1]||0},seed:e,generated_at:new Date().toISOString(),gameMode:o},P=va.initializeSpells(p,r,O),L=[];for(const q of X){const ae=l.validatePrerequisites(q,O);ae.valid||console.warn(`Feature "${q.name}" (${q.id}) failed prerequisite validation:`,ae.errors),L.push(q)}const _e=[];for(const q of de){const ae=l.validatePrerequisites(q,O);ae.valid||console.warn(`Trait "${q.name}" (${q.id}) failed prerequisite validation:`,ae.errors),_e.push(q)}const ge={name:g,race:h,subrace:c,class:p,level:r,ability_scores:f,ability_modifiers:v,proficiency_bonus:$,hp:{current:D,max:D,temp:0},armor_class:x,initiative:N,speed:I,skills:H,saving_throws:te,racial_traits:_e.map(q=>q.id),class_features:L.map(q=>q.id),appearance:Q,spells:P,equipment:ee,xp:{current:0,next_level:ct[r+1]||0},seed:e,generated_at:new Date().toISOString(),gameMode:o};if(ht.applyMultipleEffects(ge,_e),ht.applyMultipleEffects(ge,L),ee){const q=[...ee.weapons.filter(ae=>ae.equipped),...ee.armor.filter(ae=>ae.equipped),...ee.items.filter(ae=>ae.equipped)];for(const ae of q){const at=dt.getEquipmentDataStatic(ae.name);if(at){const er=ae.instanceId;le.equipItem(ge,at,er).applied||(ae.equipped=!1)}}}return va.filterCharacterSpells(ge)}}function yh(s){return typeof s=="string"&&["humanoid","beast","undead","dragon","fiend","construct","elemental","monstrosity"].includes(s)}function vh(s){return typeof s=="string"&&["common","uncommon","elite","boss"].includes(s)}function bh(s){return typeof s=="string"&&["brute","archer","support"].includes(s)}function wh(s){return typeof s=="string"&&["easy","medium","hard","deadly"].includes(s)}class Sh{static calculatePartyLevel(e){if(e.length===0)return 1;const t=e.map(a=>a.level||1);return bs(t)}static calculatePartyStrength(e){if(e.length===0)return 0;const t=e.reduce((c,u)=>c+(u.hp?.max||10),0),a=this.getAverageAC(e),i=this.getAverageDamage(e),n=e.length,r=a/10,o=t*r,l=i*n*10;return o+l}static getXPBudget(e,t){if(e.length===0)return 0;const a=e.map(i=>i.level||1);return Wt(a,t)}static getAverageAC(e){if(e.length===0)return 10;const t=e.reduce((a,i)=>a+(i.armor_class||10),0);return Math.round(t/e.length)}static getAverageHP(e){if(e.length===0)return 10;const t=e.reduce((a,i)=>a+(i.hp?.max||10),0);return Math.round(t/e.length)}static getPartySize(e){return e.length}static getAverageDamage(e){if(e.length===0)return 0;const t=e.reduce((a,i)=>a+this.estimateCharacterDamage(i),0);return Math.round(t/e.length)}static estimateCharacterDamage(e){const t=e.ability_scores,a=e.equipment?.weapons?.find(o=>o.equipped);let i,n;if(a?.damage?.dice){i=a.damage.dice;const o=a.weaponProperties?.includes("ranged")??!1,l=a.weaponProperties?.includes("finesse")??!1;n=Math.floor(((t?.[o||l?"DEX":"STR"]??10)-10)/2)}else i="1",n=Math.floor(((t?.STR??10)-10)/2);let r;try{const o=U.parseDiceFormula(i);r=o.diceCount*((o.diceSides+1)/2)+o.modifier+n}catch{r=n+1}return Math.round(r*10)/10}static analyzeParty(e){const t=this.calculatePartyLevel(e),a=this.getPartySize(e),i=this.getAverageAC(e),n=this.getAverageHP(e),r=this.getAverageDamage(e),o=this.calculatePartyStrength(e),l=this.getXPBudget(e,"easy"),c=this.getXPBudget(e,"medium"),u=this.getXPBudget(e,"hard"),d=this.getXPBudget(e,"deadly");return{averageLevel:t,partySize:a,averageAC:i,averageHP:n,averageDamage:r,totalStrength:o,easyXP:l,mediumXP:c,hardXP:u,deadlyXP:d}}}const ie=[];for(let s=0;s<256;++s)ie.push((s+256).toString(16).slice(1));function _h(s,e=0){return(ie[s[e+0]]+ie[s[e+1]]+ie[s[e+2]]+ie[s[e+3]]+"-"+ie[s[e+4]]+ie[s[e+5]]+"-"+ie[s[e+6]]+ie[s[e+7]]+"-"+ie[s[e+8]]+ie[s[e+9]]+"-"+ie[s[e+10]]+ie[s[e+11]]+ie[s[e+12]]+ie[s[e+13]]+ie[s[e+14]]+ie[s[e+15]]).toLowerCase()}let ea;const Ah=new Uint8Array(16);function kh(){if(!ea){if(typeof crypto>"u"||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");ea=crypto.getRandomValues.bind(crypto)}return ea(Ah)}const Th=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ss={randomUUID:Th};function Ch(s,e,t){s=s||{};const a=s.random??s.rng?.()??kh();if(a.length<16)throw new Error("Random bytes length must be >= 16");return a[6]=a[6]&15|64,a[8]=a[8]&63|128,_h(a)}function Ih(s,e,t){return ss.randomUUID&&!s?ss.randomUUID():Ch(s)}class Eh{constructor(e={}){this.options={validateAudioUrls:!1,strict:!1,audioUrlValidationTimeout:5e3,resolveImageUrls:!1,...e}}async parse(e){const t=e.name,a=e.image,i=e.creator,n=e.description,r=e.genre,o=e.tags;let l=a;a&&this.options.resolveImageUrls&&(l=await z.arweaveGatewayManager.resolveUrl(a));const c=e.tracks||[],u=[];for(let d=0;d<c.length;d++)try{const h=await this.parseTrack(c[d],d);if(h){const p=await this.resolveTrackUrls(h);u.push(p)}}catch(h){if(this.options.strict)throw h;console.warn(`Failed to parse track at index ${d}:`,h)}return{name:t,description:n,image:l,creator:i,genre:r,tags:o,version:e.version,playlist_type:e.playlist_type,original_playlist_tx_id:e.original_playlist_tx_id,playlist_artist:e.playlist_artist,platform:e.platform,tracks:u}}async resolveTrackUrls(e){if(!this.options.resolveImageUrls)return e;const t={...e};return t.image_url&&(t.image_url=await z.arweaveGatewayManager.resolveUrl(t.image_url)),t.image_thumb_url&&(t.image_thumb_url=await z.arweaveGatewayManager.resolveUrl(t.image_thumb_url)),t}async parseTrack(e,t){const a=B.MetadataExtractor.parseMetadata(e.metadata);if(!a&&this.options.strict)throw new Error(`Failed to parse metadata for track at index ${t}`);const i=e.chain_name,n=e.token_address,r=e.token_id,o=e.tx_id,l=e.platform,c=e.id||(i==="AR"?`AR-${o}`:`${i}-${n}-${r}`),u=e.uuid||Ih(),d=B.MetadataExtractor.extractTitle(a||{}),h=B.MetadataExtractor.extractArtist(a||{}),g=typeof e.artwork_url=="string"&&e.artwork_url||typeof e.image_url=="string"&&e.image_url||null||B.MetadataExtractor.extractImageUrl(a||{}),m=B.MetadataExtractor.extractImageThumbUrl(a||{}),v=typeof e.audio_url=="string"&&e.audio_url||null||B.MetadataExtractor.extractAudioUrl(a||{}),w=B.MetadataExtractor.extractAudioUrlLossless(a||{}),E=B.MetadataExtractor.convertAttributes(a?.attributes),I=bn(a||{}),D=wn(e.selected_mix,E,I.mixes),x=D?.audio_url||v,N=D?D.audio_url_lossless:w;if(!x){if(this.options.strict)throw new Error(`No audio URL found for track: ${d||c}`);return console.warn(`Track ${c} has no audio URL - marked as Unsummonable`),null}if(this.options.validateAudioUrls&&!await this.validateAudioUrl(x)){if(this.options.strict)throw new Error(`Audio URL validation failed for track: ${d||c}`);return console.warn(`Track ${c} has invalid audio URL - marked as Unsummonable`),null}if(!d||!h||!g){if(this.options.strict)throw new Error(`Missing required fields for track: ${JSON.stringify({title:d,artist:h,imageUrl:g})}`);return null}const $=B.MetadataExtractor.extractDescription(a||{})??void 0,H=typeof a?.album=="string"?a.album:void 0,te=a?.duration?Number(a.duration):0,Q=B.MetadataExtractor.extractGenre(a||{}),X=B.MetadataExtractor.extractTags(a||{}),de=a?.bpm?Number(a.bpm):void 0,ee=typeof a?.key=="string"?a.key:void 0,O={id:c,uuid:u,playlist_index:t,chain_name:i,platform:l,title:d,artist:h,description:$,album:H,image_url:g,audio_url:x,audio_url_lossless:N&&N!==x?N:void 0,...D?{selected_mix:D.name}:{},duration:te,genre:Q,tags:X,bpm:de,key:ee,attributes:E||void 0,extras:I};return m&&(O.image_thumb_url=m),typeof a?.audio_ipfs_hash=="string"?O.audio_ipfs_hash=e.audio_ipfs_hash||a.audio_ipfs_hash:e.audio_ipfs_hash&&(O.audio_ipfs_hash=e.audio_ipfs_hash),typeof a?.artwork_ipfs_hash=="string"?O.artwork_ipfs_hash=e.artwork_ipfs_hash||a.artwork_ipfs_hash:e.artwork_ipfs_hash&&(O.artwork_ipfs_hash=e.artwork_ipfs_hash),typeof e.mint_function=="string"&&(O.mint_function=e.mint_function),typeof e.mint_price=="string"&&(O.mint_price=e.mint_price),typeof e.mint_snapshot_time=="number"&&(O.mint_snapshot_time=e.mint_snapshot_time),typeof e.mint_token=="string"&&(O.mint_token=e.mint_token),i!=="AR"?(O.token_address=n,O.token_id=r):o&&(O.tx_id=o),O}async validateAudioUrl(e){const t=new AbortController,a=setTimeout(()=>t.abort(),this.options.audioUrlValidationTimeout);try{const i=await fetch(e,{method:"HEAD",signal:t.signal});return clearTimeout(a),i.ok}catch(i){return clearTimeout(a),i instanceof Error&&i.name==="AbortError"?console.warn(`Audio URL validation timed out after ${this.options.audioUrlValidationTimeout}ms: ${e}`):console.warn(`Audio URL validation failed for ${e}:`,i),!1}}}class Mh{constructor(e={}){this.canvas=null,this.context=null,typeof document<"u"&&(this.canvas=document.createElement("canvas"),this.canvas.width=100,this.canvas.height=100,this.context=this.canvas.getContext("2d",{willReadFrequently:!0}))}async extractPalette(e){try{if(!this.context||!this.canvas)throw new Error("Canvas not supported in this environment");const t=await z.arweaveGatewayManager.resolveUrl(e),a=await this.loadImage(t);this.context.drawImage(a,0,0,100,100);const i=this.context.getImageData(0,0,100,100),n=this.getPixels(i);if(n.length===0)return this.getFallbackPalette();let r=this.kMeans(n,4);r.length<4&&(r=this.medianCut(n,4));const o=this.sortColorsByFrequency(n,r);return this.analyzePalette(o)}catch(t){return console.warn("Color extraction failed, using fallback:",t),this.getFallbackPalette()}}loadImage(e){return new Promise((t,a)=>{const i=new Image;i.crossOrigin="Anonymous",i.onload=()=>t(i),i.onerror=n=>a(n),i.src=e})}getPixels(e){const t=[];for(let a=0;a<e.data.length;a+=4){const i=e.data[a],n=e.data[a+1],r=e.data[a+2];e.data[a+3]>=128&&t.push([i,n,r])}return t}kMeans(e,t){if(e.length===0)return[];if(e.length<t)return e;let a=[];const i=new Set;for(;a.length<t&&a.length<e.length;){const n=Math.floor(Math.random()*e.length);i.has(n)||(i.add(n),a.push([...e[n]]))}for(let n=0;n<10;n++){const r=Array.from({length:t},()=>[]);for(const l of e){let c=1/0,u=0;for(let d=0;d<a.length;d++){const h=this.distance(l,a[d]);h<c&&(c=h,u=d)}r[u].push(l)}const o=[];for(let l=0;l<t;l++)if(r[l].length===0)o.push(a[l]);else{const c=r[l].reduce((u,d)=>[u[0]+d[0],u[1]+d[1],u[2]+d[2]],[0,0,0]);o.push([Math.round(c[0]/r[l].length),Math.round(c[1]/r[l].length),Math.round(c[2]/r[l].length)])}if(o.every((l,c)=>l[0]===a[c][0]&&l[1]===a[c][1]&&l[2]===a[c][2]))break;a=o}return a}medianCut(e,t){if(e.length===0)return[];const a=[],i=[{pixels:[...e],depth:0}];for(;i.length<t&&i.length>0;){let n=i[0],r=0,o=0;for(let u=0;u<i.length;u++){const d=this.getBoxRange(i[u].pixels);d>o&&(o=d,n=i[u],r=u)}if(n.pixels.length<=1)break;const l=this.sortPixelsByChannel(n.pixels,n.depth%3),c=Math.floor(l.length/2);i.splice(r,1),i.push({pixels:l.slice(0,c),depth:n.depth+1},{pixels:l.slice(c),depth:n.depth+1})}for(const n of i)if(n.pixels.length>0){const r=n.pixels.reduce((o,l)=>[o[0]+l[0],o[1]+l[1],o[2]+l[2]],[0,0,0]);a.push([Math.round(r[0]/n.pixels.length),Math.round(r[1]/n.pixels.length),Math.round(r[2]/n.pixels.length)])}return a.slice(0,t)}getBoxRange(e){if(e.length===0)return 0;let t=1/0,a=-1/0,i=1/0,n=-1/0,r=1/0,o=-1/0;for(const[l,c,u]of e)t=Math.min(t,l),a=Math.max(a,l),i=Math.min(i,c),n=Math.max(n,c),r=Math.min(r,u),o=Math.max(o,u);return Math.max(a-t,n-i,o-r)}sortPixelsByChannel(e,t){return[...e].sort((a,i)=>a[t]-i[t])}distance(e,t){return Math.sqrt(Math.pow(e[0]-t[0],2)+Math.pow(e[1]-t[1],2)+Math.pow(e[2]-t[2],2))}sortColorsByFrequency(e,t){const a=new Map;for(const i of e){let n=t[0],r=1/0;for(const l of t){const c=this.distance(i,l);c<r&&(r=c,n=l)}const o=`${n[0]},${n[1]},${n[2]}`;a.set(o,(a.get(o)||0)+1)}return t.sort((i,n)=>{const r=`${i[0]},${i[1]},${i[2]}`,o=`${n[0]},${n[1]},${n[2]}`;return(a.get(o)||0)-(a.get(r)||0)})}analyzePalette(e){const t=e.map(c=>this.rgbToHex(c[0],c[1],c[2]));if(t.length===0)return this.getFallbackPalette();const a=t[0],i=t[1],n=t[2],r=this.getBrightness(e[0]),o=this.getSaturation(e[0]),l=o<.1;return{colors:t,primary_color:a,secondary_color:i,accent_color:n,brightness:r/255,saturation:o,is_monochrome:l}}getBrightness(e){return(e[0]*299+e[1]*587+e[2]*114)/1e3}getSaturation(e){const t=Math.max(e[0],e[1],e[2]),a=Math.min(e[0],e[1],e[2]);return t===0?0:(t-a)/t}rgbToHex(e,t,a){return"#"+[e,t,a].map(i=>{const n=i.toString(16);return n.length===1?"0"+n:n}).join("").toUpperCase()}getFallbackPalette(){return{colors:["#000000","#333333","#666666"],primary_color:"#000000",secondary_color:"#333333",accent_color:"#666666",brightness:0,saturation:0,is_monochrome:!0}}}const ta=z.Logger.for("BeatSubdivider"),Dh={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};class Qe{constructor(e){this.options={...Dh,...e},ta.debug("BeatSubdivider initialized",{options:this.options})}subdivide(e,t=y.DEFAULT_SUBDIVISION_CONFIG){if(ta.debug("Starting per-beat subdivision",{audioId:e.audioId,beatCount:e.beats.length,explicitAssignments:t.beatSubdivisions.size,defaultSubdivision:t.defaultSubdivision}),y.validateSubdivisionConfig(t),y.validateSubdivisionConfigAgainstBeats(t,e.beats.length),e.beats.length===0)return this.createEmptySubdividedBeatMap(e,t);const a=new Set;let i=0;const n=!!e.tempoSections&&e.tempoSections.length>1;let r=0;const o=m=>{let f=0;for(let v=m-1;v>=0&&(t.beatSubdivisions.get(v)??t.defaultSubdivision)==="triplet4";v--)f++;return f%2},l=[];for(let m=0;m<e.beats.length;m++){const f=t.beatSubdivisions.get(m)??t.defaultSubdivision;t.beatSubdivisions.has(m)&&r++,a.add(f);const v=y.getSubdivisionDensity(f);i=Math.max(i,v);const w=e.beats[m],E=m<e.beats.length-1?e.beats[m+1]:null,I=e.detectedBeatIndices.includes(m);if(f==="rest"||f==="half"&&w.beatInMeasure%2!==0)continue;const D=f==="dotted4"&&w.beatInMeasure%2!==0;if(f==="offbeat8"||f==="triplet4"&&o(m)===1||D||l.push({...w,isDetected:I,originalBeatIndex:m,subdivisionType:f}),E){const N=this.createInterpolatedBeatsForSubdivision(w,E,m,f,e,n,t,o(m));l.push(...N)}}const c=this.buildDetectedBeatIndices(l),u=e.beats.length,d=l.length,h=u>0?d/u:1,p={originalBeatCount:u,subdividedBeatCount:d,averageDensityMultiplier:h,explicitBeatCount:r,subdivisionsUsed:Array.from(a),hasMultipleTempos:n,maxDensity:i},g={audioId:e.audioId,duration:e.duration,beats:l,detectedBeatIndices:c,subdivisionConfig:t,downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections,subdivisionMetadata:p};return ta.debug("Per-beat subdivision complete",{originalBeats:u,subdividedBeats:d,densityMultiplier:h,subdivisionsUsed:Array.from(a)}),g}createInterpolatedBeatsForSubdivision(e,t,a,i,n,r,o,l=0){const c=[],u=n.quarterNoteInterval,d=r?this.getQuarterNoteIntervalForTimestamp(n,e.timestamp):u;switch(i){case"quarter":break;case"half":break;case"eighth":c.push(this.createInterpolatedBeat(e,t,.5,"eighth",d,this.options));break;case"sixteenth":for(let p=.25;p<1;p+=.25)c.push(this.createInterpolatedBeat(e,t,p,"sixteenth",d,this.options));break;case"triplet8":for(const p of[1/3,2/3])c.push(this.createInterpolatedBeat(e,t,p,"triplet8",d,this.options));break;case"triplet4":{l===0?c.push(this.createInterpolatedBeat(e,t,2/3,"triplet4",d,this.options)):(o.beatSubdivisions.get(a-1)??o.defaultSubdivision)==="triplet4"&&c.push(this.createInterpolatedBeat(e,t,1/3,"triplet4",d,this.options));break}case"dotted4":e.beatInMeasure%2!==0&&c.push(this.createInterpolatedBeat(e,t,.5,"dotted4",d,this.options));break;case"dotted8":c.push(this.createInterpolatedBeat(e,t,3/4,"dotted8",d,this.options));break;case"swing":c.push(this.createInterpolatedBeat(e,t,2/3,"swing",d,this.options));break;case"offbeat8":c.push(this.createInterpolatedBeat(e,t,.5,"offbeat8",d,this.options));break;case"rest":break;default:const h=i;throw new Error(`Unknown subdivision type: ${h}`)}return c}createEmptySubdividedBeatMap(e,t){return{audioId:e.audioId,duration:e.duration,beats:[],detectedBeatIndices:[],subdivisionConfig:t,downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections,subdivisionMetadata:{originalBeatCount:0,subdividedBeatCount:0,averageDensityMultiplier:1,explicitBeatCount:0,subdivisionsUsed:[t.defaultSubdivision],hasMultipleTempos:!!e.tempoSections&&e.tempoSections.length>1,maxDensity:1}}}createInterpolatedBeat(e,t,a,i,n,r){const o=t.timestamp-e.timestamp,l=e.timestamp+o*a,c=e.beatInMeasure+a,u=(e.intensity+t.intensity)/2,d=(e.confidence+t.confidence)/2;return{timestamp:l,beatInMeasure:c,isDownbeat:!1,measureNumber:e.measureNumber,intensity:u,confidence:d,isDetected:!1,subdivisionType:i}}findClosestBeat(e,t){let a=e[0],i=Math.abs(e[0].timestamp-t);for(const n of e){const r=Math.abs(n.timestamp-t);r<i&&(i=r,a=n)}return a}calculateBeatInMeasure(e,t,a){return(e-t)/a%4}calculateMeasureNumber(e,t,a,i){const r=(e-t)/a,o=i.segments[0]?.timeSignature?.beatsPerMeasure??4;return Math.floor(r/o)}buildDetectedBeatIndices(e){const t=[];for(let a=0;a<e.length;a++)e[a].isDetected&&t.push(a);return t}getTempoSectionForTimestamp(e,t){if(!(!e||e.length===0)){for(const a of e)if(t>=a.start&&t<a.end)return a;return e[e.length-1]}}getQuarterNoteIntervalForTimestamp(e,t){const a=this.getTempoSectionForTimestamp(e.tempoSections,t);return a?a.intervalSeconds:e.quarterNoteInterval}getTempoSectionForBeatIndex(e,t){if(!(!e||e.length===0)){for(const a of e)if(t>=a.startBeatIndex&&t<=a.endBeatIndex)return a;return e[e.length-1]}}static toJSON(e){const t=Array.from(e.subdivisionConfig.beatSubdivisions.entries()),a={audioId:e.audioId,duration:e.duration,beats:e.beats.map(i=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,isDetected:i.isDetected,originalBeatIndex:i.originalBeatIndex,subdivisionType:i.subdivisionType})),detectedBeatIndices:e.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:t,defaultSubdivision:e.subdivisionConfig.defaultSubdivision},downbeatConfig:e.downbeatConfig,tempoSections:e.tempoSections?.map(i=>({start:i.start,end:i.end,bpm:i.bpm,intervalSeconds:i.intervalSeconds,beatCount:i.beatCount,startBeatIndex:i.startBeatIndex,endBeatIndex:i.endBeatIndex})),subdivisionMetadata:{originalBeatCount:e.subdivisionMetadata.originalBeatCount,subdividedBeatCount:e.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:e.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:e.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:e.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:e.subdivisionMetadata.hasMultipleTempos,maxDensity:e.subdivisionMetadata.maxDensity}};return JSON.stringify(a)}static fromJSON(e){const t=JSON.parse(e),a=new Map(t.subdivisionConfig.beatSubdivisions||[]);return{audioId:t.audioId,duration:t.duration,beats:t.beats.map(i=>({timestamp:i.timestamp,beatInMeasure:i.beatInMeasure,isDownbeat:i.isDownbeat,measureNumber:i.measureNumber,intensity:i.intensity,confidence:i.confidence,requiredKey:i.requiredKey,isDetected:i.isDetected,originalBeatIndex:i.originalBeatIndex,subdivisionType:i.subdivisionType})),detectedBeatIndices:t.detectedBeatIndices,subdivisionConfig:{beatSubdivisions:a,defaultSubdivision:t.subdivisionConfig.defaultSubdivision},downbeatConfig:t.downbeatConfig,tempoSections:t.tempoSections?.map(i=>({start:i.start,end:i.end,bpm:i.bpm,intervalSeconds:i.intervalSeconds,beatCount:i.beatCount,startBeatIndex:i.startBeatIndex,endBeatIndex:i.endBeatIndex})),subdivisionMetadata:{originalBeatCount:t.subdivisionMetadata.originalBeatCount,subdividedBeatCount:t.subdivisionMetadata.subdividedBeatCount,averageDensityMultiplier:t.subdivisionMetadata.averageDensityMultiplier,explicitBeatCount:t.subdivisionMetadata.explicitBeatCount,subdivisionsUsed:t.subdivisionMetadata.subdivisionsUsed,hasMultipleTempos:t.subdivisionMetadata.hasMultipleTempos,maxDensity:t.subdivisionMetadata.maxDensity}}}static async saveToFile(e,t){if(typeof process>"u"||!process.versions?.node)throw new Error("saveToFile is only available in Node.js environment");const{writeFile:a}=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),i=Qe.toJSON(e);await a(t,i,"utf-8")}static async loadFromFile(e){if(typeof process>"u"||!process.versions?.node)throw new Error("loadFromFile is only available in Node.js environment");const{readFile:t}=await Promise.resolve().then(()=>require("./__vite-browser-external-BcPniuRQ.cjs")),a=await t(e,"utf-8");return Qe.fromJSON(a)}}class Rh{constructor(e){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0,this.previousDirection="neutral",this.options={...y.DEFAULT_GROOVE_OPTIONS,...e}}setDifficulty(e){const t=y.getGroovePenaltiesForPreset(e.preset,e.customPenalties);this.options.hotnessLossOnMiss=t.hotnessLossOnMiss,this.options.hotnessLossOnBreak=t.hotnessLossOnBreak}recordHit(e,t,a=0,i="perfect"){if(this.hitCount++,this.lastBpm=t,i==="miss"||i==="wrongKey")return this.recordMiss(a);const n=this.pocketDirection;this.recentOffsets.push(e),this.recentOffsets.length>this.options.averagingWindowSize&&this.recentOffsets.shift(),this.updateRunningAverage(),this.pocketDirection=this.determineDirection(this.establishedOffset);const r=this.calculatePocketWindow(t),o=Math.abs(e-this.establishedOffset),l=this.calculateConsistency(o,r),c=this.hasPocket()&&o<=r;this.hasPocket()&&(c?(this.hotness=this.hotness+this.options.hotnessGainPerHit,this.streakLength++):this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnBreak),this.maxGrooveStreak=Math.max(this.maxGrooveStreak,this.streakLength));let u;const d=n!==this.pocketDirection,h=d||this.hotness===0;if(this.hotness>0&&!h)this.grooveStartTime===null&&(this.grooveStartTime=a??this.hitCount*(60/t)),this.maxHotness=Math.max(this.maxHotness,this.hotness),this.hotnessSamples.push(this.hotness),this.grooveHitCount++;else if(h&&this.grooveStartTime!==null&&this.grooveHitCount>0){const p=a??this.hitCount*(60/t),g=this.hotnessSamples.length>0?this.hotnessSamples.reduce((m,f)=>m+f,0)/this.hotnessSamples.length:0;u={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:g,duration:p-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:p},this.streakLength=0,this.hotness=0,this.resetGrooveStats()}return d&&(this.streakLength=0,this.hotness=0,this.resetGrooveStats()),this.previousDirection=this.pocketDirection,{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:l,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,inPocket:c,pocketWindow:r,endedGrooveStats:u}}recordMiss(e){const a=this.hotness>0&&this.grooveStartTime!==null&&this.grooveHitCount>0;this.hotness=Math.max(0,this.hotness-this.options.hotnessLossOnMiss),this.streakLength=0;const i=this.calculatePocketWindow(this.lastBpm);let n;if(a&&this.hotness===0){const r=e??this.hitCount*(60/this.lastBpm),o=this.hotnessSamples.length>0?this.hotnessSamples.reduce((l,c)=>l+c,0)/this.hotnessSamples.length:0;n={maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:o,duration:r-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:r},this.resetGrooveStats()}return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,consistency:0,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,inPocket:!1,pocketWindow:i,endedGrooveStats:n}}getState(){const e=this.hotnessSamples.length>0?this.hotnessSamples.reduce((t,a)=>t+a,0)/this.hotnessSamples.length:0;return{pocketDirection:this.pocketDirection,establishedOffset:this.establishedOffset,hotness:this.hotness,tier:y.getGrooveTier(this.hotness),streakLength:this.streakLength,hitCount:this.hitCount,pocketWindow:this.calculatePocketWindow(this.lastBpm),grooveStartTime:this.grooveStartTime,grooveDuration:this.grooveStartTime!==null?this.grooveHitCount*(60/this.lastBpm):0,maxHotness:this.maxHotness,avgHotness:e,grooveHitCount:this.grooveHitCount}}reset(){this.recentOffsets=[],this.establishedOffset=0,this.pocketDirection="neutral",this.hotness=0,this.streakLength=0,this.hitCount=0,this.lastBpm=120,this.resetGrooveStats()}getGrooveStats(e){if(this.grooveStartTime===null||this.grooveHitCount===0)return null;const t=e??this.hitCount*(60/this.lastBpm),a=this.hotnessSamples.length>0?this.hotnessSamples.reduce((i,n)=>i+n,0)/this.hotnessSamples.length:0;return{maxStreak:this.maxGrooveStreak,maxHotness:this.maxHotness,avgHotness:a,duration:t-this.grooveStartTime,totalHits:this.grooveHitCount,startTime:this.grooveStartTime,endTime:t}}resetGrooveStats(){this.grooveStartTime=null,this.maxHotness=0,this.maxGrooveStreak=0,this.hotnessSamples=[],this.grooveHitCount=0}hasPocket(){return this.hitCount>=this.options.minHitsForPocket}calculatePocketWindow(e){const a=y.getGrooveWindowMs(this.hotness)/1e3,i=120/e;return a*i}updateRunningAverage(){this.recentOffsets.length!==0&&(this.establishedOffset=this.recentOffsets.reduce((e,t)=>e+t,0)/this.recentOffsets.length)}calculateConsistency(e,t){if(!this.hasPocket())return 0;const a=e/t;return a>=1?0:1-a*a}determineDirection(e){return Math.abs(e)<this.options.neutralDeadZone?"neutral":e<0?"push":"pull"}}function Ph(s,e=y.DEFAULT_SUBDIVISION_CONFIG,t){const a=y.unifyBeatMap(s);return new Qe(t).subdivide(a,e)}function We(s){return"mergedBeats"in s?s.mergedBeats:s.beats}function ei(s,e){return"mergedBeats"in s?{...s,mergedBeats:e}:{...s,beats:e}}function xh(s,e,t){const a=We(s);if(e<0||e>=a.length)throw new Error(`beatIndex ${e} is out of bounds (beats array length: ${a.length})`);const i=a.map((n,r)=>{if(r!==e)return n;if(t===null){const{requiredKey:o,...l}=n;return l}return{...n,requiredKey:t}});return ei(s,i)}function Nh(s,e){const t=We(s),a=t.length;for(const r of e)if(r.beatIndex<0||r.beatIndex>=a)throw new Error(`beatIndex ${r.beatIndex} is out of bounds (beats array length: ${a})`);const i=new Map;for(const{beatIndex:r,key:o}of e)i.set(r,o);const n=t.map((r,o)=>{const l=i.get(o);if(l===void 0)return r;if(l===null){const{requiredKey:c,...u}=r;return u}return{...r,requiredKey:l}});return ei(s,n)}function $h(s){const e=We(s),t=new Map;for(let a=0;a<e.length;a++){const i=e[a];i.requiredKey!==void 0&&t.set(a,i.requiredKey)}return t}function Lh(s){const t=We(s).map(a=>{if(a.requiredKey===void 0)return a;const{requiredKey:i,...n}=a;return n});return ei(s,t)}function Oh(s){return We(s).some(t=>t.requiredKey!==void 0)}function Bh(s){return We(s).filter(t=>t.requiredKey!==void 0).length}function Fh(s){const e=We(s),t=new Set;for(const a of e)a.requiredKey!==void 0&&t.add(a.requiredKey);return Array.from(t).sort()}const J=z.Logger.for("SubdivisionPlaybackController");class Hh{constructor(e,t,a={}){this.unifiedMap=e,this.audioContext=t,this.options={...y.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS,...a};const i={tolerance:.02,defaultIntensity:.5,defaultConfidence:.7};this.subdivider=new Qe(i),this.state={isRunning:!1,isPaused:!1,currentTime:0,startTime:0,pauseTime:0,rafId:null,currentSubdivision:this.options.initialSubdivision,pendingSubdivision:null,pendingMeasureNumber:null,transitionTimestamp:null,scheduledBeats:new Map,thresholds:y.getAccuracyThresholdsForPreset("medium")},this.subscribers=new Set,this.regenerateBeats(),J.debug("SubdivisionPlaybackController initialized",{audioId:e.audioId,duration:e.duration,initialSubdivision:this.state.currentSubdivision,beatCount:e.beats.length})}get subdivision(){return this.state.currentSubdivision}get beatMap(){return this.unifiedMap}getOptions(){return{...this.options}}subscribe(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}setSubdivision(e){if(!y.isValidSubdivisionType(e))throw new Error(`Invalid subdivision type: ${e}`);if(this.state.currentSubdivision===e)return;const t=this.state.currentSubdivision;switch(J.debug("Subdivision change requested",{from:t,to:e,transitionMode:this.options.transitionMode}),this.options.transitionMode){case"immediate":this.applySubdivisionChange(e,t);break;case"next-downbeat":this.state.pendingSubdivision=e,this.state.pendingMeasureNumber=null,J.debug("Subdivision change deferred until next downbeat",{pendingType:e,transitionMode:this.options.transitionMode});break;case"next-measure":{const i=this.getCurrentBeat()?.measureNumber??null;this.state.pendingSubdivision=e,this.state.pendingMeasureNumber=i,J.debug("Subdivision change deferred until next measure",{pendingType:e,transitionMode:this.options.transitionMode,currentMeasureNumber:i});break}default:this.applySubdivisionChange(e,t)}}setTransitionMode(e){if(!["immediate","next-downbeat","next-measure"].includes(e))throw new Error(`Invalid transition mode: ${e}`);const t=this.options.transitionMode;t!==e&&(J.debug("Transition mode change requested",{from:t,to:e}),this.options={...this.options,transitionMode:e},e==="immediate"&&this.state.pendingSubdivision&&(J.debug("Applying pending subdivision immediately due to mode switch",{pendingSubdivision:this.state.pendingSubdivision}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision)),J.debug("Transition mode changed",{from:t,to:e}))}applySubdivisionChange(e,t){this.state.currentSubdivision=e,this.state.pendingSubdivision=null,this.state.pendingMeasureNumber=null;const a=this.state.transitionTimestamp;if(this.state.transitionTimestamp=null,this.regenerateBeats(a),this.options.onSubdivisionChange)try{this.options.onSubdivisionChange(t,e)}catch(i){J.error("Error in onSubdivisionChange callback",i)}J.debug("Subdivision changed",{from:t,to:e,transitionTimestamp:a})}regenerateBeats(e=null){const t={beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},a=this.subdivider.subdivide(this.unifiedMap,t),i=new Map;if(e!==null)for(const[n,r]of this.state.scheduledBeats)r.beat.timestamp<e&&i.set(n,r);this.state.scheduledBeats.clear();for(const[n,r]of i)this.state.scheduledBeats.set(n,r);for(const n of a.beats){if(e!==null&&n.timestamp<e)continue;const r=this.getBeatKey(n);this.state.scheduledBeats.has(r)||this.state.scheduledBeats.set(r,{beat:n,upcomingEmitted:!1,exactEmitted:!1,passedEmitted:!1})}J.debug("Beats regenerated",{subdivision:this.state.currentSubdivision,beatCount:a.beats.length,preservedBeatCount:i.size,totalScheduledBeats:this.state.scheduledBeats.size,fromTimestamp:e})}getBeatKey(e){return`${e.timestamp.toFixed(6)}-${e.subdivisionType}`}play(){this.state.isRunning||(this.state.isRunning=!0,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.state.isPaused=!1,this.scheduleUpdate(),J.debug("Playback started",{startTime:this.state.startTime,pauseTime:this.state.pauseTime}))}stop(){this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),this.state.isRunning=!1,this.state.isPaused=!1,this.state.pauseTime=0,this.regenerateBeats(),J.debug("Playback stopped")}pause(){!this.state.isRunning||this.state.isPaused||(this.state.isPaused=!0,this.state.pauseTime=this.getCurrentAudioTime(),this.state.rafId!==null&&(cancelAnimationFrame(this.state.rafId),this.state.rafId=null),J.debug("Playback paused",{pauseTime:this.state.pauseTime}))}resume(){this.state.isPaused&&(this.state.isPaused=!1,this.state.startTime=this.audioContext.currentTime-this.state.pauseTime,this.scheduleUpdate(),J.debug("Playback resumed",{startTime:this.state.startTime}))}seek(e){const t=Math.max(0,Math.min(e,this.unifiedMap.duration));this.state.pauseTime=t,this.state.isRunning&&!this.state.isPaused&&(this.state.startTime=this.audioContext.currentTime-t),this.regenerateBeats(),J.debug("Seeked to time",{time:t})}getBeatsInRange(e,t){const a=[];for(const[,i]of this.state.scheduledBeats){const n=i.beat;n.timestamp>=e&&n.timestamp<=t&&a.push(n)}return a.sort((i,n)=>i.timestamp-n.timestamp),a}getUpcomingBeats(e){const t=this.getCurrentAudioTime(),a=[];for(const[,i]of this.state.scheduledBeats){const n=i.beat,r=n.timestamp-t;if(r>=0&&r<=this.options.anticipationTime&&(a.push(n),a.length>=e))break}return a.sort((i,n)=>i.timestamp-n.timestamp),a}getBeatAtTime(e){const t=this.options.timingTolerance;for(const[,a]of this.state.scheduledBeats)if(Math.abs(a.beat.timestamp-e)<=t)return a.beat;return null}getCurrentBeat(){const e=this.getCurrentAudioTime();let t=null;for(const[,a]of this.state.scheduledBeats)if(a.beat.timestamp<=e)t=a.beat;else break;return t}getNextBeat(){const e=this.getCurrentAudioTime(),t=Array.from(this.state.scheduledBeats.values()).map(a=>a.beat).sort((a,i)=>a.timestamp-i.timestamp);for(const a of t)if(a.timestamp>e)return a;return null}getCurrentAudioTime(){return this.state.isRunning?this.state.isPaused?this.state.pauseTime:this.audioContext.currentTime-this.state.startTime+this.getTotalLatencyCompensation():this.state.pauseTime}getTotalLatencyCompensation(){let e=0;if(this.options.compensateOutputLatency){const t=this.audioContext.outputLatency??0,a=this.audioContext.baseLatency??0;e+=t+a}return e+=this.options.userOffsetMs/1e3,e}scheduleUpdate(){!this.state.isRunning||this.state.isPaused||(this.state.rafId=requestAnimationFrame(()=>{this.update(),this.scheduleUpdate()}))}update(){if(!this.state.isRunning||this.state.isPaused)return;const e=this.getCurrentAudioTime();this.checkPendingSubdivisionChange(e);for(const t of this.state.scheduledBeats.values()){const i=t.beat.timestamp-e;!t.upcomingEmitted&&i<=this.options.anticipationTime&&i>0&&(this.emitEvent(t.beat,"upcoming",e,i),t.upcomingEmitted=!0),!t.exactEmitted&&Math.abs(i)<=this.options.timingTolerance&&(this.emitEvent(t.beat,"exact",e,i),t.exactEmitted=!0),!t.passedEmitted&&i<-this.options.timingTolerance&&(this.emitEvent(t.beat,"passed",e,i),t.passedEmitted=!0)}}checkPendingSubdivisionChange(e){if(!this.state.pendingSubdivision)return;const t=this.getCurrentBeat();if(!t)return;let a=!1;if(this.options.transitionMode==="next-downbeat")a=t.isDownbeat;else if(this.options.transitionMode==="next-measure"){const i=this.state.pendingMeasureNumber;i!==null?a=t.measureNumber>i&&Math.floor(t.beatInMeasure)===0:a=t.beatInMeasure===0}a&&(this.state.transitionTimestamp=t.timestamp,J.debug("Applying pending subdivision change",{newSubdivision:this.state.pendingSubdivision,transitionMode:this.options.transitionMode,currentMeasureNumber:t.measureNumber,pendingMeasureNumber:this.state.pendingMeasureNumber,beatInMeasure:t.beatInMeasure,transitionTimestamp:this.state.transitionTimestamp}),this.applySubdivisionChange(this.state.pendingSubdivision,this.state.currentSubdivision))}emitEvent(e,t,a,i){const n={beat:e,currentSubdivision:this.state.currentSubdivision,audioTime:a,timeUntilBeat:i,type:t};for(const r of this.subscribers)try{r(n)}catch(o){J.error("SubdivisionPlaybackController callback error:",o)}}isRunning(){return this.state.isRunning&&!this.state.isPaused}isPaused(){return this.state.isPaused}getCurrentTime(){return this.getCurrentAudioTime()}getDuration(){return this.unifiedMap.duration}checkButtonPress(e,t){const a=t??this.state.thresholds,i=Array.from(this.state.scheduledBeats.values()).map(u=>u.beat);let n=null,r=1/0;for(const u of i){const d=e-u.timestamp,h=Math.abs(d);h<r&&(r=h,n=u)}if(!n)return{accuracy:"miss",offset:e,matchedBeat:null,absoluteOffset:e,keyMatch:!0,pressedKey:void 0,requiredKey:void 0};const o=e-n.timestamp,l=Math.abs(o);let c;return l<=a.perfect?c="perfect":l<=a.great?c="great":l<=a.good?c="good":l<=a.ok?c="ok":c="miss",{accuracy:c,offset:o,matchedBeat:n,absoluteOffset:l,keyMatch:!0,pressedKey:void 0,requiredKey:void 0}}setBeatMap(e){this.unifiedMap=e,this.regenerateBeats(),J.debug("Beat map updated",{audioId:e.audioId,beatCount:e.beats.length})}forceUpdate(){this.update()}getSubdividedBeatMap(){const e=[];for(const[,i]of this.state.scheduledBeats)e.push(i.beat);e.sort((i,n)=>i.timestamp-n.timestamp);const t=new Set(e.map(i=>i.subdivisionType)),a=e.filter(i=>i.isDetected).length;return{audioId:this.unifiedMap.audioId,duration:this.unifiedMap.duration,beats:e,detectedBeatIndices:[],subdivisionConfig:{beatSubdivisions:new Map,defaultSubdivision:this.state.currentSubdivision},downbeatConfig:this.unifiedMap.downbeatConfig,subdivisionMetadata:{originalBeatCount:a,subdividedBeatCount:e.length,averageDensityMultiplier:e.length/Math.max(1,this.unifiedMap.beats.length),explicitBeatCount:0,subdivisionsUsed:Array.from(t),hasMultipleTempos:!1,maxDensity:Math.max(...Array.from(t).map(i=>i==="eighth"?2:i==="sixteenth"?4:i==="triplet8"||i==="triplet4"?3:1))}}}dispose(){this.stop(),this.subscribers.clear(),this.state.scheduledBeats.clear(),J.debug("SubdivisionPlaybackController disposed")}}const Wh=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],zh={stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5};class ti{constructor(e){this.config={level_thresholds:e?.level_thresholds||Wh,xp_per_second:e?.xp_per_second??1,xp_per_track_completion:e?.xp_per_track_completion??50,activity_bonuses:{...zh,...e?.activity_bonuses},track_mastery_threshold:e?.track_mastery_threshold??10,mastery_bonus_xp:e?.mastery_bonus_xp??100}}calculateSessionXP(e,t){let a=e.duration_seconds*this.config.xp_per_second;if(e.activity_type&&e.activity_type in this.config.activity_bonuses){const i=this.config.activity_bonuses[e.activity_type];a*=i}return e.environmental_context&&(a=this.applyEnvironmentalBonus(a,e.environmental_context)),e.gaming_context&&(a=this.applyGamingBonus(a,e.gaming_context)),e.rhythm_game_context&&(a=this.applyRhythmGameBonus(a,e.rhythm_game_context)),t&&e.duration_seconds>=t.duration*.95&&(a+=this.config.xp_per_track_completion),Math.floor(a)}applyEnvironmentalBonus(e,t){let a=1;return t.weather?.isNight&&(a*=this.config.activity_bonuses.night_time),t.weather&&(t.weather.weatherType==="Thunderstorm"&&(a*=this.config.activity_bonuses.extreme_weather),(t.weather.weatherType==="Rain"||t.weather.weatherType==="Snow")&&(a*=this.config.activity_bonuses.extreme_weather)),t.geolocation?.altitude&&t.geolocation.altitude>=2e3&&(a*=this.config.activity_bonuses.high_altitude),e*a}applyGamingBonus(e,t){if(!t.isActivelyGaming)return e;let a=1;if(a+=.25,t.currentGame){if(t.currentGame.genre){const i=t.currentGame.genre;(i.includes("Action")||i.includes("FPS"))&&(a+=.15),i.includes("RPG")&&(a+=.2),i.includes("Strategy")&&(a+=.1)}if(t.currentGame.partySize&&t.currentGame.partySize>1&&(a+=.15),t.currentGame.sessionDuration){const i=t.currentGame.sessionDuration/60;i>=1&&(a+=Math.min(.2,i*.05))}}return e*a}applyRhythmGameBonus(e,t){if(!t.isActive)return e;let a=this.config.activity_bonuses.rhythm_game_base;if(t.currentCombo>0){const i=t.currentCombo/t.maxComboCap;a+=i*this.config.activity_bonuses.rhythm_game_combo}if(t.grooveHotness>0){const i=t.grooveHotness/100;a+=i*this.config.activity_bonuses.rhythm_game_groove}return e*a}calculateTotalModifier(e,t,a){let i=1;return e&&(i*=this.calculateEnvironmentalModifier(e)),t&&t.isActivelyGaming&&(i*=this.calculateGamingModifier(t)),a&&a.isActive&&(i*=this.calculateRhythmGameModifier(a)),Math.min(i,3)}calculateEnvironmentalModifier(e){let t=1;return e.weather?.weatherType==="Thunderstorm"&&(t*=this.config.activity_bonuses.extreme_weather),e.weather?.isNight&&(t*=this.config.activity_bonuses.night_time),e.geolocation?.altitude&&e.geolocation.altitude>=2e3&&(t*=this.config.activity_bonuses.high_altitude),Math.min(t,3)}calculateGamingModifier(e){let t=1;if(t+=.25,e.currentGame){if(e.currentGame.genre){const a=e.currentGame.genre;(a.includes("Action")||a.includes("FPS"))&&(t+=.15),a.includes("RPG")&&(t+=.2),a.includes("Strategy")&&(t+=.1)}if(e.currentGame.partySize&&e.currentGame.partySize>1&&(t+=.15),e.currentGame.sessionDuration){const a=e.currentGame.sessionDuration/60;a>=1&&(t+=Math.min(.2,a*.05))}}return Math.min(t,1.75)}calculateRhythmGameModifier(e){let t=this.config.activity_bonuses.rhythm_game_base;if(e.currentCombo>0){const a=e.currentCombo/e.maxComboCap;t+=a*this.config.activity_bonuses.rhythm_game_combo}if(e.grooveHotness>0){const a=e.grooveHotness/100;t+=a*this.config.activity_bonuses.rhythm_game_groove}return Math.min(t,2.25)}getXPThresholdForLevel(e){if(e<1||e>20)throw new Error("Level must be between 1 and 20");return this.config.level_thresholds[e-1]}getXPToNextLevel(e){if(e<1||e>19)throw new Error("Current level must be between 1 and 19");const t=this.config.level_thresholds[e-1];return this.config.level_thresholds[e]-t}getLevelFromXP(e){for(let t=20;t>=1;t--)if(e>=this.config.level_thresholds[t-1])return t;return 1}isTrackMastered(e){return e>=this.config.track_mastery_threshold}getMasteryBonusXP(){return this.config.mastery_bonus_xp}getConfig(){return{...this.config}}}class qh{constructor(e){this.activeSessions=new Map,this.sessionHistory=[],this.sessionCounter=0,this.xpCalculator=e||new ti}startSession(e,t,a){const i=`session-${this.sessionCounter++}-${e}-${Date.now()}`;return this.activeSessions.set(i,{track_uuid:e,start_time:Date.now(),track:t,environmental_context:a?.environmental_context,gaming_context:a?.gaming_context}),i}endSession(e,t,a){const i=this.activeSessions.get(e);if(!i)return null;const n=Date.now(),r=t||Math.max(1,Math.ceil((n-i.start_time)/1e3)),o={track_uuid:i.track_uuid,start_time:i.start_time,end_time:n,duration_seconds:Math.round(r),base_xp_earned:0,bonus_xp:0,activity_type:a,environmental_context:i.environmental_context,gaming_context:i.gaming_context,total_xp_earned:0};return o.base_xp_earned=Math.floor(o.duration_seconds*this.xpCalculator.getConfig().xp_per_second),o.total_xp_earned=this.xpCalculator.calculateSessionXP(o,i.track),o.bonus_xp=o.total_xp_earned-o.base_xp_earned,this.sessionHistory.push(o),this.activeSessions.delete(e),o}getActiveSession(e){return this.activeSessions.get(e)||null}getActiveSessionDuration(e){const t=this.activeSessions.get(e);return t?(Date.now()-t.start_time)/1e3:null}updateSessionContext(e,t){const a=this.activeSessions.get(e);return a?(t.environmental_context&&(a.environmental_context=t.environmental_context),t.gaming_context&&(a.gaming_context=t.gaming_context),!0):!1}getSessionHistory(){return[...this.sessionHistory]}getSessionsForTrack(e){return this.sessionHistory.filter(t=>t.track_uuid===e)}getTotalListeningTime(){return this.sessionHistory.reduce((e,t)=>e+t.duration_seconds,0)}getTotalXPEarned(){return this.sessionHistory.reduce((e,t)=>e+t.total_xp_earned,0)}getTrackListeningTime(e){return this.getSessionsForTrack(e).reduce((t,a)=>t+a.duration_seconds,0)}getTrackListenCount(e){return this.getSessionsForTrack(e).length}isTrackMastered(e,t=10){return this.getTrackListenCount(e)>=t}getSessionsInRange(e,t){return this.sessionHistory.filter(a=>a.start_time>=e&&a.end_time<=t)}getAverageSessionLength(){return this.sessionHistory.length===0?0:this.getTotalListeningTime()/this.sessionHistory.length}getLongestSession(){return this.sessionHistory.length===0?null:this.sessionHistory.reduce((e,t)=>t.duration_seconds>e.duration_seconds?t:e)}getTrackXPTotal(e){return this.getSessionsForTrack(e).reduce((t,a)=>t+a.total_xp_earned,0)}clearTrackSessions(e){const t=this.sessionHistory.length;return this.sessionHistory=this.sessionHistory.filter(a=>a.track_uuid!==e),t-this.sessionHistory.length}clearHistory(){this.sessionHistory=[]}clearActiveSessions(){this.activeSessions.clear()}getActiveSessionCount(){return this.activeSessions.size}getActiveSessionIds(){return Array.from(this.activeSessions.keys())}}const ns=[4,8,12,16,19];class ke{static setUncappedConfig(e){this.uncappedConfig=e}static getUncappedConfig(){return this.uncappedConfig}static setStatManager(e){this.statManager=e}static getStatManager(){return this.statManager}static processLevelUp(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=n?1/0:20;if(t<1||t>r)throw new Error(`Level must be between 1 and ${r}`);const o=Ce[e.class];if(!o)throw new Error(`Unknown class: ${e.class}`);const l=this.calculateHPIncrease(o.hit_die,e.ability_modifiers.CON,a),c=e.hp.max+l,u=this.getProficiencyBonus(t,n),d=u-e.proficiency_bonus,h={newLevel:t,hitPointIncrease:l,newHitPointsTotal:c,proficiencyBonusIncrease:d,newProficiencyBonus:u},p=n||ns.includes(t);if(this.statManager&&p){const m=this.statManager.processLevelUp(e,t);m&&m.increases.length>0&&(h.abilityScoreIncreases=m.increases.map(f=>({ability:f.ability,increase:f.delta})),m.increases.length===1&&(h.abilityScoreIncrease={ability:m.increases[0].ability,increase:m.increases[0].delta}))}else ns.includes(t)&&(h.abilityScoreIncrease={ability:"STR",increase:2});if(this.isSpellcaster(e.class)){const m=this.calculateSpellSlots(t),f={};for(const[v,w]of Object.entries(m))f[Number(v)]={total:w,used:0};h.newSpellSlots=f}const g=this.getClassFeaturesForLevel(e,e.class,t);if(g.length>0){h.classFeatures=g.map(m=>m.id),h.featureEffects=[];for(const m of g)if(m.effects&&m.effects.length>0){const f={...e,level:t},v=ht.applyFeatureEffects(f,m);v.applied&&h.featureEffects.push({featureId:m.id,featureName:m.name,effectsApplied:v.count})}}return h}static getClassFeaturesForLevel(e,t,a){const i=re.getInstance(),n=i.getFeaturesForLevel(t,a),r={...e,level:a},o=[];for(const l of n){const c=i.validatePrerequisites(l,r);c.valid?o.push(l):(console.warn(`Feature "${l.name}" (level ${l.level}) failed prerequisite validation at level ${a}:`,c.errors),l.source==="default"&&o.push(l))}return o}static applyLevelUp(e,t){const a={...e};a.level=t.newLevel,a.hp.max=t.newHitPointsTotal,a.hp.current=Math.min(a.hp.current+t.hitPointIncrease,a.hp.max),a.proficiency_bonus=t.newProficiencyBonus;const i=a.gameMode==="uncapped"?1/0:20;if(t.abilityScoreIncreases&&t.abilityScoreIncreases.length>0)for(const n of t.abilityScoreIncreases){const r=n.ability;a.ability_scores[r]=Math.min(i,a.ability_scores[r]+n.increase);const o=a.ability_scores[r];a.ability_modifiers[r]=Math.floor((o-10)/2)}else if(t.abilityScoreIncrease){const n=t.abilityScoreIncrease.ability;a.ability_scores[n]=Math.min(i,a.ability_scores[n]+t.abilityScoreIncrease.increase);const r=a.ability_scores[n];a.ability_modifiers[n]=Math.floor((r-10)/2)}return t.newSpellSlots&&a.spells&&(a.spells.spell_slots=t.newSpellSlots),t.classFeatures&&(a.class_features=[...new Set([...a.class_features,...t.classFeatures])]),this.reapplyEquipmentEffects(a),a}static calculateHPIncrease(e,t,a){let i;return a?i=new Se(a).randomInt(1,e+1):i=Math.floor(Math.random()*e)+1,Math.max(1,i+t)}static isSpellcaster(e){return["Bard","Cleric","Druid","Paladin","Ranger","Sorcerer","Warlock","Wizard"].includes(e)}static calculateSpellSlots(e){const t={1:0};for(let a=1;a<=9;a++)t[a]=this.getSpellSlotCount(e,a);return t}static getSpellSlotCount(e,t){return t>Math.ceil(e/2)||e<1?0:e<3?t===1?2:0:e<5?t===1?3:t===2?2:0:e<7?t===1?4:t===2?3:0:e<9?t===1||t===2?4:t===3?2:0:Math.min(5,Math.ceil(e/2))}static getXPThreshold(e,t=!1){if(e<1)throw new Error("Level must be at least 1");if(!t){if(e>20)throw new Error("Standard mode only supports levels 1-20");return ct[e]}if(this.uncappedConfig?.xpFormula)return this.uncappedConfig.xpFormula(e);if(e<=20)return ct[e];let a=355e3;for(let i=21;i<=e;i++)a+=(i-1)*i*500;return a}static getProficiencyBonus(e,t=!1){if(!t){if(e<1||e>20)throw new Error("Standard proficiency bonus only defined for levels 1-20");return Ma[e]}return this.uncappedConfig?.proficiencyBonusFormula?this.uncappedConfig.proficiencyBonusFormula(e):e<=4?2:e<=8?3:e<=12?4:e<=16?5:2+Math.floor((e-1)/4)}static calculateLevel(e,t=!1){if(!t){for(let i=20;i>=1;i--)if(e>=this.getXPThreshold(i,!1))return i;return 1}let a=1;for(;e>=this.getXPThreshold(a+1,!0);)a++;return a}static getXPToNextLevel(e,t=!1){if(!t&&e>=20)return 0;const a=this.getXPThreshold(e,t);return this.getXPThreshold(e+1,t)-a}static getProgressPercentage(e,t,a=!1){if(!a&&e>=20)return 100;const i=this.getXPThreshold(e,a),n=this.getXPThreshold(e+1,a),r=t-i,o=n-i;return Math.floor(r/o*100)}static processLevelUpWithoutStats(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=n?1/0:20;if(t<1||t>r)throw new Error(`Level must be between 1 and ${r}`);const o=Ce[e.class];if(!o)throw new Error(`Unknown class: ${e.class}`);const l=this.calculateHPIncrease(o.hit_die,e.ability_modifiers.CON,a),c=e.hp.max+l,u=this.getProficiencyBonus(t,n),d=u-e.proficiency_bonus,h={newLevel:t,hitPointIncrease:l,newHitPointsTotal:c,proficiencyBonusIncrease:d,newProficiencyBonus:u};if(this.isSpellcaster(e.class)){const g=this.calculateSpellSlots(t),m={};for(const[f,v]of Object.entries(g))m[Number(f)]={total:v,used:0};h.newSpellSlots=m}const p=this.getClassFeaturesForLevel(e,e.class,t);if(p.length>0){h.classFeatures=p.map(g=>g.id),h.featureEffects=[];for(const g of p)if(g.effects&&g.effects.length>0){const m={...e,level:t},f=ht.applyFeatureEffects(m,g);f.applied&&h.featureEffects.push({featureId:g.id,featureName:g.name,effectsApplied:f.count})}}return h}static applyAutomaticBenefitsOnly(e,t){const a={...e};return a.level=t.newLevel,a.hp.max=t.newHitPointsTotal,a.hp.current=Math.min(a.hp.current+t.hitPointIncrease,a.hp.max),a.proficiency_bonus=t.newProficiencyBonus,t.newSpellSlots&&a.spells&&(a.spells.spell_slots=t.newSpellSlots),t.classFeatures&&(a.class_features=[...new Set([...a.class_features,...t.classFeatures])]),this.reapplyEquipmentEffects(a),a}static applyStatIncreasesOnly(e,t){const a={...e};a.ability_scores={...a.ability_scores},a.ability_modifiers={...a.ability_modifiers};const n=(a.gameMode||"standard")==="uncapped"?1/0:20;for(const r of t){const o=r.ability;a.ability_scores[o]=Math.min(n,a.ability_scores[o]+r.amount),a.ability_modifiers[o]=Math.floor((a.ability_scores[o]-10)/2)}return a}static reapplyEquipmentEffects(e){e.equipment&&le.reapplyEquipmentEffects(e)}}class Ln{constructor(){this.name="D&D 5e Standard"}selectIncreases(e,t,a){if(a?.forcedAbilities&&a.forcedAbilities.length>0){const i=[];for(const n of a.forcedAbilities)e.ability_scores[n]>=20||i.push({ability:n,amount:t/a.forcedAbilities.length});return i}return[]}requiresManualInput(e){return!e?.forcedAbilities||e.forcedAbilities.length===0}}class On{constructor(){this.name="D&D 5e Smart"}selectIncreases(e,t,a){const i=new Set(a?.excludedAbilities||[]),n=["STR","DEX","CON","INT","WIS","CHA"].filter(c=>!i.has(c)&&e.ability_scores[c]<20);if(n.length===0)return[];const o=Ce[e.class]?.primary_ability||"STR";if(!i.has(o)&&e.ability_scores[o]<18&&n.includes(o)){if(t===2&&this.shouldSplit(e,o)){const c=this.findLowestStat(e,n,o);return[{ability:o,amount:1},{ability:c,amount:1}]}return[{ability:o,amount:t}]}if(t===2&&n.length>=2){const c=this.findLowestStat(e,n),u=this.findLowestStat(e,n.filter(d=>d!==c));return[{ability:c,amount:1},{ability:u,amount:1}]}return[{ability:this.findLowestStat(e,n),amount:t}]}requiresManualInput(){return!1}shouldSplit(e,t){const a=e.ability_scores[t];if(a<15)return!1;const i=Object.values(e.ability_scores);return Math.min(...i)<a-4}findLowestStat(e,t,a){let i=t[0],n=e.ability_scores[i];for(const r of t){if(r===a)continue;const o=e.ability_scores[r];o<n&&(i=r,n=o)}return i}}class Bn{constructor(){this.name="Balanced"}selectIncreases(e,t,a){const i=new Set(a?.excludedAbilities||[]),n=["STR","DEX","CON","INT","WIS","CHA"].filter(l=>!i.has(l)).filter(l=>e.ability_scores[l]<20).sort((l,c)=>e.ability_scores[l]-e.ability_scores[c]);if(n.length===0)return[];if(a?.forcedAbilities&&a.forcedAbilities.length>=2)return[{ability:a.forcedAbilities[0],amount:1},{ability:a.forcedAbilities[1],amount:1}];const r=[];let o=t;for(let l=0;l<n.length&&o>0;l++)r.push({ability:n[l],amount:1}),o--;return r}requiresManualInput(){return!1}}class Fn{constructor(){this.name="Primary Only"}selectIncreases(e,t,a){const n=Ce[e.class]?.primary_ability||"STR";return a?.excludedAbilities?.includes(n)?[{ability:this.findLowestStat(e,a?.excludedAbilities),amount:t}]:e.ability_scores[n]>=20?[{ability:this.findLowestStat(e,[n]),amount:t}]:[{ability:n,amount:t}]}requiresManualInput(){return!1}findLowestStat(e,t){const a=["STR","DEX","CON","INT","WIS","CHA"].filter(r=>!t?.includes(r)).filter(r=>e.ability_scores[r]<20);if(a.length===0)return"STR";let i=a[0],n=e.ability_scores[i];for(const r of a){const o=e.ability_scores[r];o<n&&(i=r,n=o)}return i}}class Hn{constructor(){this.name="Random"}selectIncreases(e,t,a){const i=["STR","DEX","CON","INT","WIS","CHA"].filter(r=>!a?.excludedAbilities?.includes(r)).filter(r=>e.ability_scores[r]<20);if(i.length===0)return[];const n=[...i].sort(()=>Math.random()-.5);return t===2&&n.length>=2?[{ability:n[0],amount:1},{ability:n[1],amount:1}]:[{ability:n[0],amount:t}]}requiresManualInput(){return!1}}class Wn{constructor(){this.name="Manual"}selectIncreases(e,t,a){return[]}requiresManualInput(){return!0}}class Uh{constructor(e,t){this.fn=e,this.name=t||"Custom Function"}selectIncreases(e,t,a){return this.fn(e,t,a)}requiresManualInput(){return!1}}function _a(s){if(typeof s!="string"&&typeof s!="function")return s;if(typeof s=="function")return new Uh(s);switch(s){case"dnD5e":return new Ln;case"dnD5e_smart":return new On;case"balanced":return new Bn;case"primary_only":return new Fn;case"random":return new Hn;case"manual":return new Wn;default:const e=s;throw new Error(`Unknown strategy type: ${e}`)}}const rs=[4,8,12,16,19],os={strategy:"dnD5e",autoApply:!0};class zn{constructor(e){this.config=this.mergeWithDefaults(e),this.strategy=_a(this.config.strategy)}increaseStats(e,t,a="manual"){const i={...e},n=[],r=[];i.ability_scores={...i.ability_scores},i.ability_modifiers={...i.ability_modifiers};const l=(i.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;for(const{ability:c,amount:u}of t){const d=i.ability_scores[c],h=d+u,p=Math.min(h,l),g=p-d;g>0?(i.ability_scores[c]=p,i.ability_modifiers[c]=Math.floor((p-10)/2),n.push({ability:c,oldValue:d,newValue:p,delta:g})):h>l&&r.push({ability:c,attemptedValue:h,cappedAt:l})}return{character:i,increases:n,capped:r,source:a,timestamp:Date.now()}}decreaseStats(e,t,a="event"){const i=t.map(({ability:n,amount:r})=>({ability:n,amount:-r}));return this.increaseStats(e,i,a)}setStat(e,t,a,i="manual"){const n=e.ability_scores[t],r=a-n;return this.increaseStats(e,[{ability:t,amount:r}],i)}processLevelUp(e,t,a){const n=(e.gameMode||"standard")==="uncapped",r=this.config.statIncreaseLevels?.length?this.config.statIncreaseLevels:rs;if(!(n||r.includes(t)))return null;const c=this.strategy.selectIncreases(e,2,a);return c.length===0?null:this.increaseStats(e,c,"level_up")}updateConfig(e){const t=this.config.strategy;this.config=this.mergeWithDefaults(e),e.strategy&&e.strategy!==t&&(this.strategy=_a(this.config.strategy))}getConfig(){return this.config}canIncrease(e,t,a=1){const i=e.ability_scores[t],r=(e.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20;return i+a<=r}getStatCap(e,t){return(e.gameMode||"standard")==="uncapped"?1/0:this.config.maxStatCap??20}validateDnD5eStatSelection(e,t,a=2){const i=t.reduce((u,d)=>u+d.amount,0);if(i!==a)return{error:`Total stat increase must be ${a}, got ${i}`,reason:"invalid_amount",allowedPatterns:[`+${a} to one ability`,"+1 to two abilities"]};const n=[{count:1,amount:a},{count:2,amount:1}];if(!n.some(u=>t.length===u.count&&t.every(d=>d.amount===u.amount)))return{error:"Invalid pattern: must be +2 to one ability or +1 to two abilities",reason:"wrong_pattern",allowedPatterns:n.map(u=>u.count===1?`+${u.amount} to one ability`:"+1 to two abilities")};const o=this.getStatCap(e,"STR");for(const u of t){const d=e.ability_scores[u.ability];if(d+u.amount>o&&o!==1/0)return{error:`${u.ability} would exceed stat cap of ${o}`,reason:"exceeds_cap",allowedPatterns:[`Choose different abilities or use ${o-d} points`]}}const l=["STR","DEX","CON","INT","WIS","CHA"];for(const u of t)if(!l.includes(u.ability))return{error:`Invalid ability: ${u.ability}`,reason:"invalid_ability",allowedPatterns:l};const c=new Set;for(const u of t){if(c.has(u.ability))return{error:`Duplicate ability: ${u.ability}`,reason:"duplicate_ability",allowedPatterns:["Each ability can only be increased once"]};c.add(u.ability)}return{valid:!0}}mergeWithDefaults(e){return{maxStatCap:e?.maxStatCap??20,strategy:e?.strategy??os.strategy,autoApply:e?.autoApply??os.autoApply,statIncreaseLevels:e?.statIncreaseLevels??rs}}}const ui=class ui{static getPlaysThreshold(e){const t=this.customThresholds.get(e);return t?.playsThreshold!==void 0&&t.playsThreshold!==null?t.playsThreshold:Math.floor(hs*Math.pow(ia,e))}static getXPThreshold(e){const t=this.customThresholds.get(e);return t?.xpThreshold!==void 0&&t.xpThreshold!==null?t.xpThreshold:Math.floor(ps*Math.pow(ia,e))}static setCustomThresholds(e,t){this.customThresholds.set(e,{...t})}static clearCustomThresholds(e){e!==void 0?this.customThresholds.delete(e):this.customThresholds.clear()}static hasCustomThresholds(e){return this.customThresholds.has(e)}static getCustomThresholds(e){return this.customThresholds.get(e)}static isMastered(e,t,a){const i=this.getPlaysThreshold(a),n=this.getXPThreshold(a);return e>=i&&t>=n}static canPrestige(e,t,a){return e>=st?!1:this.isMastered(t,a,e)}static isJustMastered(e,t,a,i,n){const r=this.isMastered(e,a,n),o=this.isMastered(t,i,n);return!r&&o}static calculateMasteryBonus(e){return e?xs:0}static getPrestigeInfo(e,t,a){const i=this.getPlaysThreshold(e),n=this.getXPThreshold(e),r=this.isMastered(t,a,e),o=e>=st;return{prestigeLevel:e,currentPlays:t,currentXP:a,playsThreshold:i,xpThreshold:n,playsProgress:Math.min(1,t/i),xpProgress:Math.min(1,a/n),isMastered:r,canPrestige:r&&!o,isMaxPrestige:o}}static toRomanNumeral(e){return ds[e]}static getNextPrestigeLevel(e){return e>=st?null:ms(e+1)}static createSuccessResult(e,t){const a=this.toRomanNumeral(t);return{success:!0,newPrestigeLevel:t,previousPrestigeLevel:e,message:`Successfully prestiged to level ${a}! New mastery requirements: ${this.getPlaysThreshold(t)} plays, ${this.getXPThreshold(t).toLocaleString()} XP`}}static createFailureResult(e,t){return{success:!1,newPrestigeLevel:t,previousPrestigeLevel:t,message:`Prestige failed: ${e}`}}static getAllThresholds(){const e=[];for(let t=0;t<=st;t++){const a=t;e.push({level:a,plays:this.getPlaysThreshold(a),xp:this.getXPThreshold(a)})}return e}};ui.customThresholds=new Map;let se=ui;class Gh{constructor(e){this.xpCalculator=new ti,this.statManager=e,this.statManager&&ke.setStatManager(this.statManager)}addXP(e,t,a="custom"){const i={...e,xp:{...e.xp}};if(i.xp.current+=t,!this.statManager){const d=(i.gameMode||"standard")==="standard"?"dnD5e":"dnD5e_smart";this.statManager=new zn({strategy:d}),ke.setStatManager(this.statManager)}let n=!1,r;const o=[],l=i.gameMode==="uncapped",c=ke.calculateLevel(i.xp.current,l);if(c>i.level){n=!0,r=c;for(let u=i.level+1;u<=c;u++)if(this.isStatIncreaseLevel(u,l)&&this.statManager.getConfig().strategy==="dnD5e"){i.pendingStatIncreases=(i.pendingStatIncreases||0)+1;const p=ke.processLevelUpWithoutStats(i,u),g=ke.applyAutomaticBenefitsOnly(i,p),m={fromLevel:u-1,toLevel:u,hpIncrease:p.hitPointIncrease,newMaxHP:p.newHitPointsTotal,proficiencyIncrease:p.proficiencyBonusIncrease,newProficiency:p.newProficiencyBonus,statIncreases:void 0,featuresGained:p.classFeatures,newSpellSlots:p.newSpellSlots};o.push(m),i.level=u,i.hp=g.hp,i.proficiency_bonus=g.proficiency_bonus,i.class_features=g.class_features,g.spells&&(i.spells=g.spells)}else{const p={...i.ability_scores},g=[...i.class_features],m=ke.processLevelUp(i,u),f=ke.applyLevelUp(i,m),v={fromLevel:u-1,toLevel:u,hpIncrease:m.hitPointIncrease,newMaxHP:m.newHitPointsTotal,proficiencyIncrease:m.proficiencyBonusIncrease,newProficiency:m.newProficiencyBonus,statIncreases:m.abilityScoreIncreases?.map(w=>({ability:w.ability,oldValue:p[w.ability],newValue:p[w.ability]+w.increase,delta:w.increase})),featuresGained:m.classFeatures?.filter(w=>!g.includes(w)),newSpellSlots:m.newSpellSlots};o.push(v),i.level=f.level,i.hp=f.hp,i.ability_scores=f.ability_scores,i.ability_modifiers=f.ability_modifiers,i.proficiency_bonus=f.proficiency_bonus,i.class_features=f.class_features,f.spells&&(i.spells=f.spells)}}return!l&&i.level>=20?i.xp.next_level=0:i.xp.next_level=ke.getXPThreshold(i.level+1,l),{character:i,xpEarned:t,leveledUp:n,newLevel:r,levelUpDetails:o.length>0?o:void 0}}addRhythmXP(e,t,a="rhythm_game"){const i=this.addXP(e,t.finalXP,a);return{character:i.character,xpEarned:i.xpEarned,leveledUp:i.leveledUp,newLevel:i.newLevel,levelUpDetails:i.levelUpDetails}}isStatIncreaseLevel(e,t){return t?!0:[4,8,12,16,19].includes(e)}updateCharacterFromSession(e,t,a,i){const n=i?.previousListenCount??0,r=i?.previousXP??0,o=i?.prestigeLevel??e.prestige_level??0,l=this.xpCalculator.calculateSessionXP(t,a);let c=!1,u=0;if(a){const p=n+1,g=r+l;se.isJustMastered(n,p,r,g,o)&&(c=!0,u=se.calculateMasteryBonus(!0))}const d=l+u;return{...this.addXP(e,d,"listening"),masteredTrack:c,masteryBonusXP:u}}applyPendingStatIncrease(e,t,a){const i=e.pendingStatIncreases||0;if(i===0)throw new Error("No pending stat increases to apply");let n;if(a&&a.length>0){if(a.length>1)throw new Error("Only one secondary stat allowed for +1/+1 distribution");n=[{ability:t,amount:1},{ability:a[0],amount:1}]}else n=[{ability:t,amount:2}];const r=this.statManager.validateDnD5eStatSelection(e,n);if("error"in r)throw new Error(r.error);const o=ke.applyStatIncreasesOnly(e,n);o.pendingStatIncreases=i-1,o.pendingStatIncreases===0&&delete o.pendingStatIncreases;const l=n.map(c=>({ability:c.ability,oldValue:e.ability_scores[c.ability],newValue:o.ability_scores[c.ability],delta:c.amount}));return{character:o,statIncreases:l,remainingPending:o.pendingStatIncreases||0,timestamp:Date.now()}}hasPendingStatIncreases(e){return!!(e.pendingStatIncreases&&e.pendingStatIncreases>0)}getPendingStatIncreaseCount(e){return e.pendingStatIncreases||0}resetCharacterForPrestige(e,t,a,i,n){const r=e.prestige_level??0;if(r>=10)return se.createFailureResult("Already at maximum prestige level",r);const o=t.getTrackListenCount(a),l=t.getTrackXPTotal(a);if(!se.canPrestige(r,o,l)){const p=se.getPlaysThreshold(r),g=se.getXPThreshold(r);return o<p&&l<g?se.createFailureResult(`Need ${p-o} more plays and ${(g-l).toLocaleString()} more XP to prestige`,r):o<p?se.createFailureResult(`Need ${p-o} more plays to prestige (XP requirement met)`,r):se.createFailureResult(`Need ${(g-l).toLocaleString()} more XP to prestige (plays requirement met)`,r)}const c=e.equipment?{weapons:[...e.equipment.weapons],armor:[...e.equipment.armor],items:[...e.equipment.items],totalWeight:e.equipment.totalWeight,equippedWeight:e.equipment.equippedWeight}:null;t.clearTrackSessions(a);const u=se.getNextPrestigeLevel(r);if(u===null)return se.createFailureResult("Cannot prestige beyond maximum level",r);const d=Vt.generate(e.seed,i,n,{level:1,gameMode:e.gameMode,forceName:e.name});return c&&(d.equipment=c),d.prestige_level=u,{...se.createSuccessResult(r,u),character:d}}canPrestige(e,t,a){const i=e.prestige_level??0,n=t.getTrackListenCount(a),r=t.getTrackXPTotal(a);return se.canPrestige(i,n,r)}getPrestigeInfo(e,t,a){const i=e.prestige_level??0,n=t.getTrackListenCount(a),r=t.getTrackXPTotal(a);return se.getPrestigeInfo(i,n,r)}}class jh{constructor(e){this.sessionTotals=null,this.sessionStartTime=null,this.config=Pt(e)}calculateButtonPressXP(e,t){const a=t?.comboLength??0,i=t?.grooveHotness,n=this.getBaseXP(e),r=n*this.config.xpRatio,o=this.config.combo.enabled?this.getComboMultiplier(a):1;let l=0;this.config.groove.perHitMultiplier&&i!==void 0&&i>0&&(l=i/100*this.config.groove.perHitScale);const c=Math.min(o+l,this.config.maxMultiplier),u=n*c,d=r*c,h=Math.max(0,d);return{scorePoints:n,baseXP:r,comboMultiplier:o,grooveMultiplier:l,totalMultiplier:c,finalScore:u,finalXP:h,breakdown:{accuracy:e,comboLength:a,grooveHotness:i}}}calculateComboEndBonus(e){if(!this.config.combo.endBonus.enabled||e<=0)return{comboLength:e,bonusScore:0,bonusXP:0};const t=this.config.combo.endBonus.formula?this.config.combo.endBonus.formula(e):e*2,a=t*this.config.xpRatio;return{comboLength:e,bonusScore:t,bonusXP:a}}calculateGrooveEndBonus(e){if(!this.config.groove.endBonus.enabled)return{bonusScore:0,bonusXP:0};if(e.maxStreak<2)return{bonusScore:0,bonusXP:0};const{maxStreakWeight:a,avgHotnessWeight:i,durationWeight:n}=this.config.groove.endBonus,r=e.maxStreak*a,o=e.avgHotness*i,l=e.duration*n,c=r+o+l,u=c*this.config.xpRatio;return{bonusScore:c,bonusXP:u}}getBaseXP(e){return this.config.baseXP[e]}getComboMultiplier(e){if(!this.config.combo.enabled)return 1;const t=this.config.combo.formula?this.config.combo.formula(e):this.defaultComboFormula(e);return Math.min(t,this.config.combo.cap)}defaultComboFormula(e){return 1+e/25}startSession(){this.sessionTotals=this.createEmptyTotals(),this.sessionStartTime=Date.now()}recordHit(e,t){this.sessionTotals||this.startSession();const a=this.calculateButtonPressXP(e,t);this.sessionTotals.totalScore+=a.finalScore,this.sessionTotals.totalXP+=a.finalXP;const i=t?.comboLength??0;return i>this.sessionTotals.maxCombo&&(this.sessionTotals.maxCombo=i),this.sessionTotals.accuracyDistribution[e]++,this.recalculateAccuracyPercentage(),a}getSessionTotals(){return!this.sessionTotals||!this.sessionStartTime?null:{...this.sessionTotals,duration:(Date.now()-this.sessionStartTime)/1e3}}endSession(){const e=this.getSessionTotals();return this.sessionTotals=null,this.sessionStartTime=null,e}createEmptyTotals(){return{totalScore:0,totalXP:0,maxCombo:0,accuracyDistribution:{perfect:0,great:0,good:0,ok:0,miss:0,wrongKey:0},accuracyPercentage:0,duration:0}}recalculateAccuracyPercentage(){if(!this.sessionTotals)return;const{perfect:e,great:t,good:a,ok:i,miss:n,wrongKey:r}=this.sessionTotals.accuracyDistribution,o=e+t+a+i+n+r;if(o===0){this.sessionTotals.accuracyPercentage=0;return}const l=e+t+a+i;this.sessionTotals.accuracyPercentage=l/o*100}getConfig(){return this.config}updateConfig(e){this.config=Pt({...this.config,...e})}}class Vh{static enchant(e,t,a,i){return Dt.validateModification(a).valid?this.addModificationToEquipment(e,t,a,i):e}static applyTemplate(e,t,a,i){const o=R.getInstance().get("equipment.templates").find(d=>d.id===a);if(!o)return e;const l={id:`template_${a}_${Date.now()}`,name:o.name||`Template: ${a}`,properties:o.properties||[],addsFeatures:o.addsFeatures,addsSkills:o.addsSkills,addsSpells:o.addsSpells,appliedAt:new Date().toISOString(),source:"template"},c=this.addModificationToEquipment(e,t,l,i),u=this.findItem(c,t);return u&&(u.templateId=a),c}static curse(e,t,a,i){return Dt.validateModification(a).valid?this.addModificationToEquipment(e,t,a,i):e}static upgrade(e,t,a,i){return this.enchant(e,t,a,i)}static removeModification(e,t,a,i){const n={weapons:e.weapons.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),armor:e.armor.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),items:e.items.map(d=>({...d,modifications:d.modifications?[...d.modifications]:[]})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight},r=this.findItem(n,t);if(!r||!r.modifications)return e;const o=r.equipped,l=r.instanceId,c=this.getEquipmentData(t);o&&i&&c&&le.unequipItem(i,t,l);const u=r.modifications.findIndex(d=>d.id===a);if(u!==-1&&r.modifications.splice(u,1),o&&i&&c){le.equipItem(i,c,l);for(const d of r.modifications)this.applyModificationEffects(i,c,d,l)}return n}static getModificationHistory(e,t){const a=this.findItem(e,t);return!a||!a.modifications?[]:a.modifications.map(i=>({...i}))}static getCombinedEffects(e,t,a){const i=this.getEquipmentData(t);if(!i)return[];const n=i.properties?[...i.properties]:[],r=this.findItem(e,t);if(r&&r.modifications)for(const o of r.modifications)o.properties&&n.push(...o.properties);return n}static hasTemplate(e,t,a){const i=this.findItem(e,t);return i?i.templateId===a?!0:i.modifications?i.modifications.some(n=>n.source==="template"&&n.name.includes(a)):!1:!1}static getAppliedTemplates(e,t){const a=[],i=this.findItem(e,t);if(!i)return a;if(i.templateId&&a.push(i.templateId),i.modifications){for(const n of i.modifications)if(n.source==="template"){const r=n.id.match(/template_([^_]+)/);r&&a.push(r[1])}}return a}static removeAllModifications(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications||i.modifications.length===0)return e;const n=i.modifications.map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static disenchant(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications)return e;const n=i.modifications.filter(o=>o.source==="enchantment"||o.source==="upgrade").map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static liftCurse(e,t,a){const i=this.findItem(e,t);if(!i||!i.modifications)return e;const n=i.modifications.filter(o=>o.source==="curse").map(o=>o.id);let r=e;for(const o of n)r=this.removeModification(r,t,o,a);return r}static addModificationToEquipment(e,t,a,i){const n={weapons:e.weapons.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),armor:e.armor.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),items:e.items.map(u=>({...u,modifications:u.modifications?[...u.modifications]:[]})),totalWeight:e.totalWeight,equippedWeight:e.equippedWeight},r=this.findItem(n,t);if(!r)return e;const o=this.getEquipmentData(t);if(!o)return e;r.modifications||(r.modifications=[]),r.instanceId||(r.instanceId=`${t}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`);const l=r.equipped,c=r.instanceId;if(l&&i&&le.unequipItem(i,t,c),r.modifications.push(a),l&&i){le.equipItem(i,o,c);for(const u of r.modifications)this.applyModificationEffects(i,o,u,c)}return n}static findItem(e,t){return e.weapons.find(i=>i.name===t)||e.armor.find(i=>i.name===t)||e.items.find(i=>i.name===t)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static applyModificationEffects(e,t,a,i){const n={...t,properties:[...t.properties||[],...a.properties],grantsFeatures:a.addsFeatures,grantsSkills:a.addsSkills,grantsSpells:a.addsSpells};le.equipItem(e,n,i)}static createModification(e,t,a,i){return{id:e,name:t,properties:a,appliedAt:new Date().toISOString(),source:i}}static createFeatureModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsFeatures:i,appliedAt:new Date().toISOString(),source:n}}static createSkillModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsSkills:i,appliedAt:new Date().toISOString(),source:n}}static createSpellModification(e,t,a,i,n){return{id:e,name:t,properties:a,addsSpells:i,appliedAt:new Date().toISOString(),source:n}}static generateModificationId(e="mod"){return`${e}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}static getModificationSources(e,t){const a=this.findItem(e,t);if(!a||!a.modifications)return[];const i=new Set(a.modifications.map(n=>n.source));return Array.from(i)}static countModificationsBySource(e,t){const a=this.findItem(e,t);if(!a||!a.modifications)return{};const i={};for(const n of a.modifications)i[n.source]=(i[n.source]||0)+1;return i}static countModificationsForSource(e,t,a){return this.countModificationsBySource(e,t)[a]||0}static isCursed(e,t){return this.countModificationsForSource(e,t,"curse")>0}static isEnchanted(e,t){return this.getModificationSources(e,t).some(i=>i==="enchantment"||i==="upgrade")}static getItemSummary(e,t){const a=this.findItem(e,t);return a?{name:a.name,quantity:a.quantity,equipped:a.equipped,instanceId:a.instanceId,templateId:a.templateId,modificationCount:a.modifications?.length||0,isCursed:this.isCursed(e,t),isEnchanted:this.isEnchanted(e,t),sources:this.getModificationSources(e,t),effects:this.getCombinedEffects(e,t)}:null}}const Ft={plus_one_weapon:{rarity:"uncommon",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+1"]},plus_two_weapon:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+2"]},plus_three_weapon:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],tags:["magic","enhanced","+3"]},flaming_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft, dim 20ft"}],tags:["magic","fire","flaming"]},frost_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],tags:["magic","cold","frost"]},shocking_weapon_template:{rarity:"rare",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage",condition:{type:"on_hit",value:!0}}],tags:["magic","lightning","shocking"]},vicious_weapon_template:{rarity:"rare",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"extra_damage_on_hit",value:"1d8",description:"Deals 1d8 extra damage, but wielder takes 1d8 necrotic",condition:{type:"on_hit",value:!0}}],tags:["magic","vicious","necrotic"]},plus_one_armor:{rarity:"rare",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 AC bonus",stackable:!0}],tags:["magic","enhanced","+1","armor"]},plus_two_armor:{rarity:"very_rare",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 AC bonus",stackable:!0}],tags:["magic","enhanced","+2","armor"]}};class qn{static openBox(e,t,a){if(!e.boxContents)return{success:!1,items:[],gold:0,consumeBox:!1,error:{code:"NO_BOX_CONTENTS",message:`Box "${e.name}" has no contents defined.`}};if(a!==void 0&&e.boxContents.openRequirements){const c=this.checkRequirements(e,a);if(c)return{success:!1,items:[],gold:0,consumeBox:!1,error:c}}const i=e.boxContents,n=[];let r=0;for(const c of i.drops){const u=this.selectFromPool(c.pool,t);if(u){if(u.gold!==void 0&&u.gold>0)r+=u.gold;else if(u.itemName){const d=this.getEquipmentData(u.itemName);if(d){const h=u.quantity??1;if(d.type==="box")n.push({...d});else for(let p=0;p<h;p++)n.push(d)}else console.warn(`BoxOpener: Item "${u.itemName}" not found in equipment registry. Skipping drop.`)}}}const o=i.consumeOnOpen!==!1;let l;return a!==void 0&&i.openRequirements&&(l=i.openRequirements.map(c=>({name:c.itemName,quantity:c.quantity??1}))),{success:!0,items:n,gold:r,consumeBox:o,consumedItems:l}}static selectFromPool(e,t){if(!e||e.length===0)return;const a=e.map(i=>[i,i.weight]);return t.weightedChoice(a)}static getEquipmentData(e){return R.getInstance().get("equipment").find(i=>i.name===e)}static isBox(e){return e.type==="box"&&e.boxContents!==void 0}static checkRequirements(e,t){if(!e.boxContents?.openRequirements)return null;const a=e.boxContents.openRequirements;for(const i of a){const n=i.quantity??1,r=t.find(o=>o.name===i.itemName);if(!r)return{code:"MISSING_ITEM",message:`Missing required item: ${i.itemName}`,requirement:i};if(r.quantity<n)return{code:"INSUFFICIENT_QUANTITY",message:`Insufficient ${i.itemName}: have ${r.quantity}, need ${n}`,requirement:i}}return null}static canOpen(e,t){return e.boxContents?.openRequirements?this.checkRequirements(e,t)===null:!0}static getRequirementsDescription(e){if(!e.boxContents?.openRequirements||e.boxContents.openRequirements.length===0)return null;const t=e.boxContents.openRequirements,a=[];for(const i of t){const n=i.quantity??1;n===1?a.push(i.itemName):a.push(`${n} ${i.itemName}`)}return`Requires: ${a.join(", ")}`}static previewContents(e){if(!e.boxContents)return{possibleItems:[],possibleGold:{min:0,max:0},totalDrops:0};const t=new Set;let a=0,i=0;for(const r of e.boxContents.drops)for(const o of r.pool)o.itemName&&t.add(o.itemName),o.gold!==void 0&&(a=Math.min(a,o.gold),i=Math.max(i,o.gold));const n={possibleItems:Array.from(t),possibleGold:{min:a,max:i},totalDrops:e.boxContents.drops.length};return e.boxContents.openRequirements&&e.boxContents.openRequirements.length>0&&(n.openRequirements=e.boxContents.openRequirements),n}}const ze={common:0,uncommon:1,rare:2,very_rare:3,legendary:4};class Yh{static spawnFromList(e,t){const i=R.getInstance().get("equipment");let n=e;return t&&(n=t.shuffle([...e])),n.map(r=>i.find(o=>o.name===r))}static spawnByRarity(e,t,a){const r=R.getInstance().get("equipment").filter(d=>d.rarity===e&&(d.spawnWeight??1)>0);if(r.length===0)return[];const o=Math.min(t,r.length),l=[],c=a?()=>a.random():Math.random,u=[...r];for(let d=u.length-1;d>0;d--){const h=Math.floor(c()*(d+1));[u[d],u[h]]=[u[h],u[d]]}for(let d=0;d<o;d++)l.push(u[d]);return l}static spawnByTags(e,t,a,i){const o=R.getInstance().get("equipment").filter(d=>{if(i?.excludeZeroWeight&&(d.spawnWeight??1)<=0||i?.includeTypes&&i.includeTypes.length>0&&!i.includeTypes.includes(d.type))return!1;const h=ze[d.rarity];return i?.minRarity&&h<ze[i.minRarity]||i?.maxRarity&&h>ze[i.maxRarity]?!1:d.tags&&e.some(p=>d.tags.includes(p))});if(o.length===0)return[];const l=Math.min(t,o.length),c=[],u=a?()=>a.random():Math.random;for(let d=0;d<l;d++){const h=o.map(f=>[f,f.spawnWeight??1]);let p=h.reduce((f,[,v])=>f+v,0),g=u()*p,m;for(const[f,v]of h)if(g-=v,g<=0){m=f;break}if(m){c.push(m);const f=o.indexOf(m);f>-1&&o.splice(f,1)}}return c}static spawnRandom(e,t,a){const r=R.getInstance().get("equipment").filter(l=>{const c=l.spawnWeight??1;if(a?.excludeZeroWeight&&c<=0||a?.includeTypes&&a.includeTypes.length>0&&!a.includeTypes.includes(l.type))return!1;const u=ze[l.rarity];return a?.minRarity&&u<ze[a.minRarity]||a?.maxRarity&&u>ze[a.maxRarity]?!1:c>0});if(r.length===0)return[];const o=[];for(let l=0;l<e&&r.length>0;l++){const c=r.map(m=>[m,m.spawnWeight??1]),u=c.reduce((m,[,f])=>m+f,0);let d=t.random()*u,h=0;for(let m=0;m<c.length;m++)if(d-=c[m][1],d<=0){h=m;break}const p=c[h][0];o.push(p);const g=r.indexOf(p);g>-1&&r.splice(g,1)}return o}static spawnFromTemplate(e,t){const a=R.getInstance();let i;const n=a.get("equipment.templates");if(Array.isArray(n)?i=n.find(o=>o.id===e||o.name===e):n&&typeof n=="object"&&(i=n[e]),!i&&e in Ft&&(i=Ft[e]),!i)return null;let r;return t?r=a.get("equipment").find(l=>l.name===t):r=this.getDefaultItemForTemplate(i),r?{...r,...i,name:`${r.name} (${e.replace(/_/g," ")})`,templateId:e,properties:[...r.properties||[],...i.properties||[]],tags:[...r.tags||[],...i.tags||[]]}:null}static spawnTreasureHoard(e,t){const a=[];let i=0;const n=this.getTreasureItemCount(e,t);for(const[r,o]of Object.entries(n))for(let l=0;l<o;l++){const c=r,u=this.spawnByRarity(c,1,t);u.length>0&&(a.push(u[0]),i+=this.estimateItemValue(u[0]))}if(a.length===0){const r=this.spawnByRarity("common",1,t);r.length>0&&(a.push(r[0]),i+=this.estimateItemValue(r[0]))}return{items:a,totalValue:i,cr:e}}static addToCharacter(e,t,a=!1){e.equipment||(e.equipment={weapons:[],armor:[],items:[],totalWeight:0,equippedWeight:0});for(const i of t){const n={name:i.name,quantity:1,equipped:a,instanceId:`${i.name}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`};switch(i.type){case"weapon":e.equipment.weapons.push(n);break;case"armor":e.equipment.armor.push(n);break;case"item":case"box":e.equipment.items.push(n);break}e.equipment.totalWeight+=i.weight,a&&(e.equipment.equippedWeight+=i.weight)}return e}static openBoxForCharacter(e,t,a){if(!e.equipment?.items||e.equipment.items.findIndex(d=>d.name===t)===-1)return null;const o=R.getInstance().get("equipment").find(d=>d.name===t);if(!o||o.type!=="box")return null;const l=e.equipment.items,c=qn.openBox(o,a,l);if(!c.success)return{character:e,result:c};if(c.consumedItems&&c.consumedItems.length>0)for(const d of c.consumedItems)e=this.consumeItemFromCharacter(e,d.name,d.quantity).character;if(c.consumeBox&&e.equipment?.items){const d=e.equipment.items.findIndex(h=>h.name===t);d!==-1&&(e.equipment.items.splice(d,1),e.equipment.totalWeight-=o.weight)}const u=c.items;return e=this.addToCharacter(e,u,!1),{character:e,result:c}}static consumeItemFromCharacter(e,t,a){if(!e.equipment?.items)return{success:!1,character:e};const i=e.equipment.items.findIndex(u=>u.name===t);if(i===-1)return{success:!1,character:e};const n=e.equipment.items[i];if(n.quantity<a)return{success:!1,character:e};const c=R.getInstance().get("equipment").find(u=>u.name===t)?.weight??0;return n.quantity-=a,e.equipment.totalWeight-=c*a,n.quantity<=0&&e.equipment.items.splice(i,1),{success:!0,character:e}}static getDefaultItemForTemplate(e){const a=R.getInstance().get("equipment");let i="item";return e.tags&&(e.tags.includes("weapon")?i="weapon":e.tags.includes("armor")&&(i="armor")),a.find(n=>n.type===i)}static getTreasureItemCount(e,t){const a={},i=()=>t.random();return e<5?(a.common=Math.floor(i()*3)+1,a.uncommon=i()>.7?1:0):e<11?(a.common=Math.floor(i()*2)+1,a.uncommon=Math.floor(i()*2)+1,a.rare=i()>.6?1:0):e<17?(a.uncommon=Math.floor(i()*2)+1,a.rare=Math.floor(i()*2)+1,a.very_rare=i()>.7?1:0):(a.rare=Math.floor(i()*2)+1,a.very_rare=Math.floor(i()*2)+1,a.legendary=i()>.5?1:0),a}static estimateItemValue(e){let a={common:50,uncommon:400,rare:4e3,very_rare:4e4,legendary:2e5}[e.rarity]||50;return e.type==="weapon"?a*=1.2:e.type==="armor"&&(a*=1.5),e.weight>20&&(a*=1.1),Math.round(a)}}const aa="geolocation_cache";class ls{constructor(e,t){if(this.cache=null,this.cacheTTL=300*1e3,this.cacheStats={hits:0,misses:0},this.logger=z.Logger.for("GeolocationProvider"),typeof e=="number"||e===void 0)this.cacheTTL=(e??5)*60*1e3,this.useLocalStorage=(t??!0)&&this.isLocalStorageAvailable();else{const a=e;this.cacheTTL=a.cacheTTL??300*1e3,this.useLocalStorage=(a.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const e="__localStorage_test__";return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return!1}}loadFromLocalStorage(){try{const e=localStorage.getItem(aa);if(e){const t=JSON.parse(e),a=Date.now(),i=Object.values(t)[0];i&&a-i.timestamp<this.cacheTTL&&(this.cache=i)}}catch(e){this.logger.warn("Failed to load geolocation cache from localStorage",{error:e})}}saveToLocalStorage(){if(!(!this.useLocalStorage||!this.cache))try{const e={position:this.cache};localStorage.setItem(aa,JSON.stringify(e))}catch(e){this.logger.warn("Failed to save geolocation cache to localStorage",{error:e})}}isCacheEntryValid(e){return e?Date.now()-e.timestamp<this.cacheTTL:!1}getCacheAge(){return this.cache?Date.now()-this.cache.timestamp:null}async getCurrentPosition(e=!1){return!e&&this.isCacheEntryValid(this.cache)?(this.cacheStats.hits++,this.cache.data):(this.cacheStats.misses++,typeof navigator>"u"||!navigator.geolocation?null:new Promise(t=>{navigator.geolocation.getCurrentPosition(a=>{const i={latitude:a.coords.latitude,longitude:a.coords.longitude,altitude:a.coords.altitude,accuracy:a.coords.accuracy,heading:a.coords.heading,speed:a.coords.speed,timestamp:a.timestamp};this.cache={data:i,timestamp:Date.now()},this.saveToLocalStorage(),t(i)},a=>{this.logger.warn("Geolocation error",{error:a.message}),t(null)},{enableHighAccuracy:!0,timeout:5e3,maximumAge:0})}))}invalidateCache(){if(this.cache=null,this.useLocalStorage)try{localStorage.removeItem(aa)}catch(e){this.logger.warn("Failed to clear geolocation cache from localStorage",{error:e})}}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}isCacheExpired(){return!this.isCacheEntryValid(this.cache)}getCachedPosition(){return this.cache?.data??null}getBiome(e,t,a=null){const i=Math.abs(e),n=this.normalizeLongitude(t),r=this.isCoastal(e,t),o=r?"_coastal":"";if(a!==null&&!isNaN(a)){const l=this.getElevationBiome(a,o);if(l)return l}if(i>66.5)return`tundra${o}`;if(this.isInSwampRegion(e,n))return`swamp${o}`;if(i>=15&&i<=45&&this.isInDesertRegion(e,n))return r?"coastal_desert":"desert";if(i<=23.5){if(this.isInJungleRegion(e,n))return`jungle${o}`;if(this.isInSavannaRegion(e,n))return`savanna${o}`;if(e>0){if(n>=290&&n<=310)return`forest${o}`;if(n>=10&&n<=30)return`forest${o}`;if(n>=70&&n<=120)return`forest${o}`}else{if(n>=10&&n<=30)return`forest${o}`;if(n>=100&&n<=140)return`forest${o}`}return`forest${o}`}if(e>0&&i>=30&&i<=50&&(n>=235&&n<=290||n>=0&&n<=40||n>=110&&n<=145))return r?"coastal_urban":"urban";if(i>23.5&&i<=66.5)if(e>0){if(this.isInTaigaRegion(e,n))return`taiga${o}`;if(n>=235&&n<=290)return i>=45?`forest${o}`:`plains${o}`;if(n>=0&&n<=40)return`forest${o}`;if(n>40&&n<=180)return i>=50?`mountain${o}`:`plains${o}`}else{if(n>=280&&n<=320)return`plains${o}`;if(n>=15&&n<=40)return`plains${o}`;if(n>=110&&n<=180)return`plains${o}`}return`plains${o}`}getElevationBiome(e,t){return e>3500?`mountain${t}`:e>1500?`mountain${t}`:e<0?`valley${t}`:null}normalizeLongitude(e){let t=e%360;return t<0&&(t+=360),t}isInJungleRegion(e,t){return Math.abs(e)>15?!1:e>=-15&&e<=5&&t>=290&&t<=310||e>=-5&&e<=5&&t>=10&&t<=30||e>=-10&&e<=10&&t>=95&&t<=140}isInSwampRegion(e,t){return e>=25&&e<=26&&t>=279&&t<=281||e>=-20&&e<=-18&&t>=22&&t<=24||e>=21&&e<=22&&t>=89&&t<=90||e>=-20&&e<=-15&&t>=300&&t<=305}isInTaigaRegion(e,t){return e<50||e>70?!1:e>=50&&e<=70&&t>=230&&t<=300||e>=60&&e<=70&&t>=5&&t<=30||e>=55&&e<=70&&t>=30&&t<=180}isInSavannaRegion(e,t){return Math.abs(e)>20?!1:e>=-5&&e<=15&&t>=30&&t<=40||e>=-20&&e<=-15&&t>=15&&t<=35||e>=-25&&e<=-5&&t>=300&&t<=315||e>=-20&&e<=-10&&t>=130&&t<=135}isInDesertRegion(e,t){return e>15&&e<30&&(t>=345||t<=40)||e>15&&e<30&&t>=35&&t<=55||e>28&&e<35&&t>=35&&t<=40||e>25&&e<35&&t>=50&&t<=65||e>23&&e<30&&t>=68&&t<=75||e>35&&e<45&&t>=100&&t<=115||e<-20&&e>-30&&t>=115&&t<=145||e<-20&&e>-25&&t>=290&&t<=292||e>25&&e<35&&t>=245&&t<=250||e<-20&&e>-30&&t>=20&&t<=30}isCoastal(e,t){const a=Math.abs(e),i=this.normalizeLongitude(t);return!!(this.isInSmallIslandRegion(e,i)||this.isInNarrowLandmass(e,i)||a>60||e>30&&e<45&&i>=0&&i<=25||e>30&&e<40&&i>=25&&i<=35||e>15&&e<30&&i>=35&&i<=43||e>24&&e<30&&i>=48&&i<=55||e>41&&e<47&&i>=27&&i<=42||e>36&&e<47&&i>=46&&i<=55||e>53&&e<66&&i>=15&&i<=30||e>50&&e<60&&i>=0&&i<=10||e>15&&e<25&&i>=65&&i<=75||e>15&&e<23&&i>=80&&i<=90||e>35&&e<43&&i>=130&&i<=142||e>10&&e<25&&i>=105&&i<=122||e>20&&e<30&&i>=265&&i<=280||e>15&&e<25&&i>=275&&i<=300)}isInSmallIslandRegion(e,t){return e>=50&&e<=60&&t>=358&&t<=360||e>=50&&e<=60&&t>=0&&t<=10||e>=30&&e<=46&&t>=128&&t<=146||e>=4&&e<=22&&t>=116&&t<=127||e>=-10&&e<=6&&t>=94&&t<=142||e>=-47&&e<=-34&&t>=165&&t<=179||e>=-26&&e<=-12&&t>=43&&t<=51||e>=63&&e<=67&&t>=338&&t<=344||e>=10&&e<=23&&t>=275&&t<=300||e>=5&&e<=10&&t>=79&&t<=82||e>=18&&e<=23&&t>=204&&t<=206||e>=-18&&e<=-12&&t>=176&&t<=181}isInNarrowLandmass(e,t){return e>=7&&e<=18&&t>=275&&t<=290||e>=33&&e<=43&&t>=124&&t<=132||e>=36&&e<=47&&t>=8&&t<=19||e>=36&&e<=44&&t>=358&&t<=360||e>=36&&e<=44&&t>=0&&t<=10||e>=55&&e<=71&&t>=4&&t<=32||e>=24&&e<=31&&t>=268&&t<=272||e>=55&&e<=62&&t>=210&&t<=220||e>=50&&e<=62&&t>=156&&t<=163}}class Zh{constructor(){this.logger=z.Logger.for("MotionDetector"),this.isListening=!1,this.lastMotion=null,this.motionCallback=null,this.lastProcessedTime=0,this.minSampleIntervalMs=83,this.deltaBuffer=[],this.bufferSize=24,this.confirmedActivity="unknown",this.candidateActivity=null,this.candidateStartTime=0,this.confirmationMs=1500,this.handleMotion=e=>{if(!this.isListening||!this.motionCallback)return;const t=Date.now(),a={acceleration:{x:e.acceleration?.x??null,y:e.acceleration?.y??null,z:e.acceleration?.z??null},accelerationIncludingGravity:{x:e.accelerationIncludingGravity?.x??0,y:e.accelerationIncludingGravity?.y??0,z:e.accelerationIncludingGravity?.z??0},rotationRate:{alpha:e.rotationRate?.alpha??null,beta:e.rotationRate?.beta??null,gamma:e.rotationRate?.gamma??null},interval:e.interval,timestamp:t};if(this.lastMotion=a,t-this.lastProcessedTime<this.minSampleIntervalMs)return;this.lastProcessedTime=t;const i=a.accelerationIncludingGravity;if(i.x!=null&&i.y!=null&&i.z!=null&&!(i.x===0&&i.y===0&&i.z===0)){const n=Math.sqrt(i.x**2+i.y**2+i.z**2),r=Math.abs(n-9.8);this.updateSmoothing(r)}this.logger.debug("Motion event received",{acceleration:a.accelerationIncludingGravity,rotation:a.rotationRate,activity:this.detectActivity(a)}),this.motionCallback(a)}}startMonitoring(e){if(typeof window>"u"||!("DeviceMotionEvent"in window)){this.logger.warn("DeviceMotionEvent not supported");return}this.motionCallback=e,this.isListening=!0,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null,window.addEventListener("devicemotion",this.handleMotion)}stopMonitoring(){typeof window<"u"&&window.removeEventListener("devicemotion",this.handleMotion),this.isListening=!1,this.motionCallback=null,this.deltaBuffer=[],this.confirmedActivity="unknown",this.candidateActivity=null}getLastMotion(){return this.lastMotion}detectActivity(e){const t=e.accelerationIncludingGravity;if(t.x==null||t.y==null||t.z==null||t.x===0&&t.y===0&&t.z===0)return"unknown";if(this.deltaBuffer.length>=10)return this.confirmedActivity;const a=Math.sqrt(t.x**2+t.y**2+t.z**2),i=Math.abs(a-9.8);return i<.5?"stationary":i<2?"walking":i<5?"running":"driving"}updateSmoothing(e){this.deltaBuffer.push(e),this.deltaBuffer.length>this.bufferSize&&this.deltaBuffer.shift();const t=this.deltaBuffer.reduce((i,n)=>i+n,0)/this.deltaBuffer.length;let a;if(t<.5?a="stationary":t<2?a="walking":t<5?a="running":a="driving",a===this.confirmedActivity){this.candidateActivity=null;return}if(a!==this.candidateActivity){this.candidateActivity=a,this.candidateStartTime=Date.now();return}Date.now()-this.candidateStartTime>=this.confirmationMs&&(this.confirmedActivity=a,this.candidateActivity=null)}}const Un=W({id:A(),main:T(),description:T(),icon:T().optional()}),Gn=W({temp:A(),feels_like:A().optional(),temp_min:A().optional(),temp_max:A().optional(),pressure:A(),humidity:A().nonnegative(),sea_level:A().optional(),grnd_level:A().optional(),temp_kf:A().optional()}),jn=W({speed:A().nonnegative(),deg:A().min(0).max(360).optional(),gust:A().optional()}),Vn=W({all:A().min(0).max(100)}),Xh=W({type:A().optional(),id:A().optional(),country:T().optional(),sunrise:A().nonnegative(),sunset:A().nonnegative(),pod:T().optional()}),Yn=W({"1h":A().optional(),"3h":A().optional()}),Zn=W({"1h":A().optional(),"3h":A().optional()}),Xn=W({lon:A().min(-180).max(180),lat:A().min(-90).max(90)}),Kh=W({coord:Xn.optional(),weather:ne(Un).min(1),base:T().optional(),main:Gn,visibility:A().nonnegative().optional(),wind:jn.optional(),rain:Yn.optional(),snow:Zn.optional(),clouds:Vn.optional(),dt:A().nonnegative(),sys:Xh,timezone:A().optional(),id:A().optional(),name:T().optional(),cod:A().optional()}),Jh=W({dt:A().nonnegative(),main:Gn,weather:ne(Un).min(1),clouds:Vn.optional(),wind:jn.optional(),visibility:A().nonnegative().optional(),pop:A().min(0).max(1).optional(),rain:Yn.optional(),snow:Zn.optional(),sys:W({pod:T().optional()}).optional(),dt_txt:T().optional()}),Qh=W({cod:Ha([T(),A()]),message:A().optional(),cnt:A().nonnegative().optional(),list:ne(Jh).min(1),city:W({id:A(),name:T(),coord:Xn,country:T().optional(),population:A().optional(),timezone:A().optional(),sunrise:A().optional(),sunset:A().optional()}).optional()}),cs="weather_api_cache";class ep{constructor(e,t,a){if(this.baseUrl="https://api.openweathermap.org/data/2.5/weather",this.forecastUrl="https://api.openweathermap.org/data/2.5/forecast",this.cache=new Map,this.forecastCache=new Map,this.cacheTTL=720*1e3,this.forecastCacheTTL=3600*1e3,this.cacheStats={hits:0,misses:0},this.logger=z.Logger.for("WeatherAPIClient"),this.lastKnownLocation=null,this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[],this.maxRecentSamples=100,typeof e=="string"||e===void 0)this.apiKey=e??"",this.cacheTTL=(t??12)*60*1e3,this.useLocalStorage=(a??!0)&&this.isLocalStorageAvailable();else{const i=e;this.apiKey=i.apiKey??"",this.cacheTTL=i.cacheTTL??720*1e3,this.useLocalStorage=(i.useLocalStorage??!0)&&this.isLocalStorageAvailable()}this.useLocalStorage&&this.loadFromLocalStorage()}isLocalStorageAvailable(){try{const e="__localStorage_test__";return localStorage.setItem(e,e),localStorage.removeItem(e),!0}catch{return!1}}loadFromLocalStorage(){try{const e=localStorage.getItem(cs);if(e){const t=JSON.parse(e),a=Date.now();for(const[i,n]of Object.entries(t))a-n.timestamp<this.cacheTTL&&this.cache.set(i,n)}}catch(e){this.logger.warn("Failed to load weather cache from localStorage",{error:e})}}saveToLocalStorage(){if(this.useLocalStorage)try{const e={};for(const[t,a]of this.cache.entries())e[t]=a;localStorage.setItem(cs,JSON.stringify(e))}catch(e){this.logger.warn("Failed to save weather cache to localStorage",{error:e})}}startTiming(){const e=performance.now();return t=>{performance.now()-e}}recordWeatherApiCall(e,t){const a=Date.now();t?(this.weatherApiMetrics.successCount++,this.weatherApiMetrics.totalTime+=e,this.weatherApiMetrics.minTime=Math.min(this.weatherApiMetrics.minTime,e),this.weatherApiMetrics.maxTime=Math.max(this.weatherApiMetrics.maxTime,e),this.recentWeatherApiTimes.push(e),this.recentWeatherApiTimes.length>this.maxRecentSamples&&this.recentWeatherApiTimes.shift()):this.weatherApiMetrics.errorCount++,this.weatherApiMetrics.lastCallTimestamp=a}recordForecastApiCall(e,t){const a=Date.now();t?(this.forecastApiMetrics.successCount++,this.forecastApiMetrics.totalTime+=e,this.forecastApiMetrics.minTime=Math.min(this.forecastApiMetrics.minTime,e),this.forecastApiMetrics.maxTime=Math.max(this.forecastApiMetrics.maxTime,e),this.recentForecastApiTimes.push(e),this.recentForecastApiTimes.length>this.maxRecentSamples&&this.recentForecastApiTimes.shift()):this.forecastApiMetrics.errorCount++,this.forecastApiMetrics.lastCallTimestamp=a}calculatePercentile(e,t){if(e.length===0)return 0;const a=[...e].sort((n,r)=>n-r),i=Math.ceil(t/100*a.length)-1;return a[Math.max(0,i)]}getPerformanceStatistics(e,t){const a=e.successCount+e.errorCount,i=e.successCount>0?e.totalTime/e.successCount:0,n=a>0?e.successCount/a*100:0;return{average:Math.round(i),min:e.minTime===1/0?0:Math.round(e.minTime),max:Math.round(e.maxTime),totalCalls:a,successRate:Math.round(n*10)/10}}getWeatherApiMetrics(){return{...this.weatherApiMetrics}}getWeatherApiStatistics(){return{...this.getPerformanceStatistics(this.weatherApiMetrics,this.recentWeatherApiTimes),p95:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentWeatherApiTimes,99))}}getForecastApiMetrics(){return{...this.forecastApiMetrics}}getForecastApiStatistics(){return{...this.getPerformanceStatistics(this.forecastApiMetrics,this.recentForecastApiTimes),p95:Math.round(this.calculatePercentile(this.recentForecastApiTimes,95)),p99:Math.round(this.calculatePercentile(this.recentForecastApiTimes,99))}}resetPerformanceMetrics(){this.weatherApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.forecastApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentWeatherApiTimes=[],this.recentForecastApiTimes=[]}getCacheKey(e,t){const a=Math.round(e*1e4)/1e4,i=Math.round(t*1e4)/1e4;return`${a},${i}`}isCacheEntryValid(e){return Date.now()-e.timestamp<this.cacheTTL}calculateMoonPhase(e){const t=new Date("2024-01-11T11:57:00Z"),a=29.530588853*24*60*60*1e3,n=(e.getTime()-t.getTime())/a;return Math.abs(n%1)}getSolarInfo(e,t,a){const i=a??new Date;return{...this.calculateSolarInfo(e,t,i),fromApi:!1,timestamp:Date.now()}}calculateSolarInfo(e,t,a){const i=this.calculateSunriseSunset(e,t,a,!0),n=this.calculateSunriseSunset(e,t,a,!1),r=this.calculateSolarNoon(e,t,a),o=this.calculateTwilight(e,t,a,"civil",!0),l=this.calculateTwilight(e,t,a,"civil",!1),{altitude:c,azimuth:u}=this.calculateSunPosition(e,t,a),d=!isNaN(n.getTime())&&!isNaN(i.getTime())?(n.getTime()-i.getTime())/(1e3*60*60):0,h=this.determineDayStage(a,i,n,o,l);return{currentTime:a,stage:h,sunrise:i,sunset:n,solarNoon:r,civilDawn:o,civilDusk:l,sunAltitude:c,sunAzimuth:u,dayLengthHours:d}}toRadians(e){return e*(Math.PI/180)}toDegrees(e){return e*(180/Math.PI)}calculateJulianDate(e){const t=e.getUTCFullYear(),a=e.getUTCMonth()+1,i=e.getUTCDate(),n=e.getUTCHours()+e.getUTCMinutes()/60+e.getUTCSeconds()/3600;let r=Math.floor((14-a)/12),o=t+4800-r,l=a+12*r-3;return i+Math.floor((153*l+2)/5)+365*o+Math.floor(o/4)-Math.floor(o/100)+Math.floor(o/400)-32045+(n-12)/24}calculateJulianCentury(e){return(e-2451545)/36525}calculateSolarDeclination(e){const t=(280.46646+e*(36000.76983+e*3032e-7))%360,a=357.52911+e*(35999.05029-1537e-7*e),i=Math.sin(this.toRadians(a))*(1.914602-e*(.004817+14e-6*e))+Math.sin(this.toRadians(2*a))*(.019993-101e-6*e)+Math.sin(this.toRadians(3*a))*289e-6,n=t+i,o=23+(26+(21.448-e*(46.815+e*(59e-5-e*.001813)))/60)/60+.00256*Math.cos(this.toRadians(125.04-1934.136*e)),l=n-.00569-.00478*Math.sin(this.toRadians(125.04-1934.136*e));return this.toDegrees(Math.asin(Math.sin(this.toRadians(o))*Math.sin(this.toRadians(l))))}calculateEquationOfTime(e){const t=(280.46646+e*(36000.76983+e*3032e-7))%360,a=357.52911+e*(35999.05029-1537e-7*e),i=.016708634-e*(42037e-9+1267e-10*e);return Math.tan(this.toRadians(23.44/2))**2,4*this.toDegrees(Math.atan2(Math.sin(this.toRadians(2*t))*Math.cos(this.toRadians(a))*2*i-Math.sin(this.toRadians(a))*2*i+Math.sin(this.toRadians(t))*Math.cos(this.toRadians(a))*2*i*Math.cos(this.toRadians(t)),1-Math.sin(this.toRadians(t))*Math.sin(this.toRadians(t))*i*i*Math.cos(this.toRadians(a))*Math.cos(this.toRadians(a))))}calculateHourAngle(e,t,a=90.833){const i=this.toRadians(e),n=this.toRadians(t),r=this.toRadians(a),o=(Math.cos(r)-Math.sin(i)*Math.sin(n))/(Math.cos(i)*Math.cos(n));return o>1?NaN:o<-1?NaN:this.toDegrees(Math.acos(o))}calculateSunriseSunset(e,t,a,i){const n=new Date(a);n.setUTCHours(12,0,0,0);const r=this.calculateJulianDate(n),o=this.calculateJulianCentury(r),l=this.calculateSolarDeclination(o),c=this.calculateHourAngle(e,l);if(isNaN(c))return new Date(NaN);const u=this.calculateEquationOfTime(o),d=(i?-c:c)/15,p=12-t/15-u/60+d,g=Math.floor(p),m=Math.floor((p-g)*60),f=Math.floor(((p-g)*60-m)*60),v=new Date(a);return v.setUTCHours(g,m,f,0),v}calculateSolarNoon(e,t,a){const i=new Date(a);i.setUTCHours(12,0,0,0);const n=this.calculateJulianDate(i),r=this.calculateJulianCentury(n),o=this.calculateEquationOfTime(r),l=12-t/15-o/60,c=Math.floor(l),u=Math.floor((l-c)*60),d=Math.floor(((l-c)*60-u)*60),h=new Date(a);return h.setUTCHours(c,u,d,0),h}calculateTwilight(e,t,a,i,n){const o={civil:96,nautical:102,astronomical:108}[i],l=new Date(a);l.setUTCHours(12,0,0,0);const c=this.calculateJulianDate(l),u=this.calculateJulianCentury(c),d=this.calculateSolarDeclination(u),h=this.calculateHourAngle(e,d,o);if(isNaN(h))return new Date(NaN);const p=this.calculateEquationOfTime(u),g=(n?-h:h)/15,f=12-t/15-p/60+g,v=Math.floor(f),w=Math.floor((f-v)*60),E=Math.floor(((f-v)*60-w)*60),I=new Date(a);return I.setUTCHours(v,w,E,0),I}calculateSunPosition(e,t,a){const i=this.calculateJulianDate(a),n=this.calculateJulianCentury(i),r=this.calculateSolarDeclination(n),o=this.calculateEquationOfTime(n),u=(a.getUTCHours()+a.getUTCMinutes()/60+a.getUTCSeconds()/3600+t/15+o/60-12)*15,d=this.toRadians(e),h=this.toRadians(r),p=this.toRadians(u),g=Math.sin(d)*Math.sin(h)+Math.cos(d)*Math.cos(h)*Math.cos(p),m=this.toDegrees(Math.asin(g)),f=(Math.sin(h)-Math.sin(d)*g)/(Math.cos(d)*Math.cos(this.toRadians(m)));let v=this.toDegrees(Math.acos(Math.max(-1,Math.min(1,f))));return u>0&&(v=360-v),{altitude:m,azimuth:v}}determineDayStage(e,t,a,i,n){const r=e.getTime();if(isNaN(t.getTime())||isNaN(a.getTime())){const d=e.getUTCMonth();return d>=4&&d<=7?"day":"night"}const o=i&&!isNaN(i.getTime())?i.getTime():t.getTime(),l=n&&!isNaN(n.getTime())?n.getTime():a.getTime(),c=t.getTime(),u=a.getTime();return r<o||r>l?"night":r>=o&&r<c?"dawn":r>u&&r<=l?"dusk":"day"}async getWeather(e,t){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;this.lastKnownLocation={latitude:e,longitude:t};const a=this.getCacheKey(e,t),i=this.cache.get(a);if(i&&this.isCacheEntryValid(i))return this.cacheStats.hits++,i.data;this.cacheStats.misses++;const n=performance.now();let r=!1;try{const o=`${this.baseUrl}?lat=${e}&lon=${t}&appid=${this.apiKey}&units=metric`,l=await fetch(o);if(!l.ok)throw new Error(`Weather API error: ${l.statusText}`);const c=await l.json(),u=Kh.safeParse(c);if(!u.success)return this.logger.error("Weather API response validation failed",{errors:u.error.issues,response:c}),null;const d=u.data,h=Date.now()/1e3,p=h<d.sys.sunrise||h>d.sys.sunset,g={temperature:d.main.temp,humidity:d.main.humidity,pressure:d.main.pressure,weatherType:d.weather[0]?.main||"Clear",windSpeed:d.wind?.speed??0,windDirection:d.wind?.deg??0,isNight:p,moonPhase:this.calculateMoonPhase(new Date),timestamp:Date.now()};return this.cache.set(a,{data:g,timestamp:Date.now()}),this.saveToLocalStorage(),r=!0,g}catch(o){return this.logger.error("Failed to fetch weather",{error:o}),null}finally{const o=performance.now()-n;this.recordWeatherApiCall(o,r)}}invalidateCache(){this.cache.clear(),this.saveToLocalStorage()}invalidateLocation(e,t){const a=this.getCacheKey(e,t);this.cache.delete(a),this.saveToLocalStorage()}getCacheStats(){return{...this.cacheStats}}resetCacheStats(){this.cacheStats={hits:0,misses:0}}clearExpiredEntries(){let e=0;for(const[t,a]of this.cache.entries())this.isCacheEntryValid(a)||(this.cache.delete(t),e++);return e>0&&this.saveToLocalStorage(),e}getCacheSize(){return this.cache.size}getLastKnownLocation(){return this.lastKnownLocation?{...this.lastKnownLocation}:null}isForecastCacheEntryValid(e){return Date.now()-e.timestamp<this.forecastCacheTTL}getForecastCacheKey(e,t,a){const i=Math.round(e*1e4)/1e4,n=Math.round(t*1e4)/1e4;return`forecast_${i},${n}_${a}h`}async getForecast(e,t,a=24){if(!this.apiKey)return this.logger.warn("Weather API key not provided"),null;const i=Math.min(a,120),n=this.getForecastCacheKey(e,t,i),r=this.forecastCache.get(n);if(r&&this.isForecastCacheEntryValid(r))return r.data.slice(0,Math.ceil(i/3));const o=performance.now();let l=!1;try{const c=Math.ceil(i/3),u=`${this.forecastUrl}?lat=${e}&lon=${t}&appid=${this.apiKey}&units=metric&cnt=${c}`,d=await fetch(u);if(!d.ok)throw new Error(`Weather Forecast API error: ${d.statusText}`);const h=await d.json(),p=Qh.safeParse(h);if(!p.success)return this.logger.error("Weather Forecast API response validation failed",{errors:p.error.issues,response:h}),null;const m=p.data.list.map(f=>({temperature:f.main.temp,humidity:f.main.humidity,pressure:f.main.pressure,weatherType:f.weather[0]?.main||"Clear",windSpeed:f.wind?.speed??0,windDirection:f.wind?.deg??0,timestamp:Date.now(),forecastTime:new Date(f.dt*1e3),probabilityOfPrecipitation:f.pop??0}));return this.forecastCache.set(n,{data:m,timestamp:Date.now()}),l=!0,m}catch(c){return this.logger.error("Failed to fetch weather forecast",{error:c}),null}finally{const c=performance.now()-o;this.recordForecastApiCall(c,l)}}async getUpcomingWeather(e,t,a=12){const i=await this.getForecast(e,t,a);if(!i||i.length===0)return null;let n=!1,r=!1,o=0,l=0;const c=[];for(const p of i)c.push(p.weatherType),(p.weatherType.toLowerCase().includes("rain")||p.weatherType.toLowerCase().includes("drizzle"))&&(n=!0,o=Math.max(o,p.probabilityOfPrecipitation)),p.weatherType.toLowerCase().includes("snow")&&(r=!0,l=Math.max(l,p.probabilityOfPrecipitation));const u={thunderstorm:5,snow:4,rain:3,drizzle:2,clouds:1,clear:0,mist:1,fog:1,haze:1,smoke:1,dust:1,sand:1,ash:1,squall:2,tornado:5};let d="Clear",h=-1;for(const p of new Set(c)){const g=u[p.toLowerCase()]??0;g>h&&(h=g,d=p)}return{willRain:n,willSnow:r,rainProbability:o,snowProbability:l,worstWeatherType:d}}detectSevereWeather(e){const t=e.weatherType.toLowerCase(),a=e.windSpeed*3.6;if(t.includes("snow")||t.includes("blizzard")){const i=t.includes("blizzard")||t.includes("heavy")||e.windSpeed>8,n=a>25;if(t.includes("blizzard")||i&&n)return{type:"Blizzard",xpBonus:.5,severity:a>50?"extreme":"high",message:"⚠️ Blizzard conditions detected! Stay safe and warm.",detectedAt:Date.now()}}if(a>118){const n=(this.lastKnownLocation?this.isTropicalRegion(this.lastKnownLocation.latitude):!1)?"Hurricane":"Typhoon";return{type:n,xpBonus:.75,severity:a>177?"extreme":"high",message:`🌀 ${n} conditions detected! Please seek shelter.`,detectedAt:Date.now()}}return t.includes("tornado")?{type:"Tornado",xpBonus:1,severity:"extreme",message:"🌪️ TORNADO WARNING! Take immediate shelter!",detectedAt:Date.now()}:t.includes("thunderstorm")&&a>60?{type:"Tornado",xpBonus:.5,severity:"high",message:"⛈️ Extreme thunderstorm with high winds! Exercise caution.",detectedAt:Date.now()}:null}isTropicalRegion(e){return Math.abs(e)<23.5}getSafetyWarning(e){switch(e.type){case"Blizzard":return e.severity==="extreme"?"🚨 EXTREME BLIZZARD: Stay indoors, avoid travel. Keep emergency supplies ready.":"⚠️ Blizzard: Dress warmly, avoid unnecessary travel. Check on neighbors.";case"Hurricane":case"Typhoon":return e.severity==="extreme"?"🚨 EXTREME CYCLONE: Seek shelter immediately! Follow evacuation orders.":"⚠️ Hurricane/Typhoon: Secure property, prepare emergency kit, follow local alerts.";case"Tornado":return"🚨 TORNADO: Take shelter in basement or interior room immediately! Stay away from windows.";default:return"⚠️ Severe weather detected. Stay informed and stay safe."}}invalidateForecastCache(){this.forecastCache.clear()}invalidateForecastLocation(e,t){for(const[a]of this.forecastCache.entries()){const i=this.getCacheKey(e,t);a.includes(i)&&this.forecastCache.delete(a)}}clearExpiredForecastEntries(){let e=0;for(const[t,a]of this.forecastCache.entries())this.isForecastCacheEntryValid(a)||(this.forecastCache.delete(t),e++);return e}}const S={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",cyan:"\x1B[36m"},ai={useColors:!0,compact:!1,showTimestamp:!0,maxFailures:5};function ii(s){return s.useColors===!1?!1:typeof process<"u"&&process.stdout?.isTTY===!0}function k(s,e,t){return t?`${e}${s}${S.reset}`:s}function tp(s,e){switch(s){case"healthy":return k("●",S.green,e);case"degraded":return k("●",S.yellow,e);case"failed":return k("●",S.red,e);default:return k("○",S.dim,e)}}function ap(s,e){return s?k("✓ Granted",S.green,e):k("✗ Denied",S.red,e)}function ip(s,e){return s?k("Available",S.green,e):k("Unavailable",S.red,e)}function si(s){const e=new Date(s),t=e.getHours().toString().padStart(2,"0"),a=e.getMinutes().toString().padStart(2,"0"),i=e.getSeconds().toString().padStart(2,"0");return`${t}:${a}:${i}`}function rt(s){return s<1e3?`${s}ms`:s<6e4?`${(s/1e3).toFixed(1)}s`:s<36e5?`${Math.floor(s/6e4)}m ${Math.floor(s%6e4/1e3)}s`:`${Math.floor(s/36e5)}h ${Math.floor(s%36e5/6e4)}m`}function us(s,e){const t=s+e;return t===0?"N/A":`${(s/t*100).toFixed(1)}%`}function Ht(s,e){const t=[],a=s.totalCalls>0;if(t.push(` Calls: ${s.totalCalls}`),t.push(` Success: ${a?`${s.successRate.toFixed(1)}%`:"N/A"}`),a){const i=s.average<500?S.green:s.average<1500?S.yellow:S.red;t.push(` Avg Time: ${k(`${s.average}ms`,i,e)}`),t.push(` Min/Avg/Max: ${s.min}/${s.average}/${s.max}ms`),t.push(` P95/P99: ${s.p95}/${s.p99}ms`)}else t.push(" Avg Time: N/A");return t}function he(s,e="─"){return e.repeat(s)}function ni(s,e,t){const a=Math.max(0,e-s.length-2),i=Math.floor(a/2),n=a-i,r=he(e),o=t?S.bright+S.cyan+s+S.reset:s;return`
|
|
50
50
|
${r}
|
|
51
51
|
${" ".repeat(i)}${o}${" ".repeat(n)}
|
|
52
52
|
${r}
|
|
@@ -80,4 +80,4 @@ ${k("┌─ API PERFORMANCE",S.cyan,a)}`),console.log(he(20)+(a?S.reset:"")),con
|
|
|
80
80
|
${he(i)}
|
|
81
81
|
`)}function Kn(s,e={}){const t={...ai,...e},a=ii(t);if(console.log(ni("SENSOR SYSTEM DASHBOARD",70,a)),t.showTimestamp){const n=s.environmental?.timestamp||s.gaming?.timestamp||Date.now();console.log(`${k("Generated:",S.dim,a)} ${si(n)}
|
|
82
82
|
`)}if(console.log(k("┌─ QUICK HEALTH SUMMARY",S.cyan,a)),console.log(he(26)+(a?S.reset:"")),s.environmental){let n=0;for(const o of s.environmental.sensors)o.status.health==="healthy"&&n++;const r=n===s.environmental.sensors.length;console.log(`
|
|
83
|
-
Environmental: ${r?k(`✓ ${n}/${n} Healthy`,S.green,a):k(`⚠ ${n}/${s.environmental.sensors.length} Healthy`,S.yellow,a)}`)}if(s.gaming){const n=s.gaming.steam.isAuthenticated;console.log(` Gaming: ${n?k("✓ Connected",S.green,a):k("✗ Disconnected",S.dim,a)}`)}console.log(""),s.environmental&&ri(s.environmental,t),s.gaming&&oi(s.gaming,t)}const li={displayEnvironmentalDiagnostics:ri,displayGamingDiagnostics:oi,displaySystemDashboard:Kn};class sp{constructor(e,t){this.permissions=new Map,this.logger=z.Logger.for("EnvironmentalSensors"),this.context={timestamp:Date.now()},this.sensorStatuses=new Map,this.failureLog=[],this.lastKnownGood=new Map,this.recoveryCallbacks=new Set,this.retryConfig={enabled:!0,maxRetries:3,initialDelayMs:1e3,maxDelayMs:1e4,backoffMultiplier:2},this.xpConfig={maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},this.permissions.set("geolocation",!1),this.permissions.set("motion",!1),this.permissions.set("weather",!1);let a="",i;if(typeof e=="string"||e===void 0)a=e??"",t&&(this.retryConfig={...this.retryConfig,...t});else{const n=e;a=n.weather?.apiKey??"",i=n.geolocation,n.retry&&(this.retryConfig={...this.retryConfig,...n.retry}),n.xpModifier&&(this.xpConfig={...this.xpConfig,...n.xpModifier})}this.geolocation=i?new ls(i):new ls,this.motion=new Zh,this.weather=new ep(a),this.initializeSensorStatuses()}initializeSensorStatuses(){const e=["geolocation","motion","weather"];for(const t of e)this.sensorStatuses.set(t,{type:t,health:"unknown",lastSuccessTimestamp:null,lastFailureTimestamp:null,consecutiveFailures:0,totalFailures:0,lastError:null,isRetrying:!1})}updateSensorStatus(e,t,a){const i=this.sensorStatuses.get(e);if(!i)return;const n=Date.now(),r=i.health;if(t)i.lastSuccessTimestamp=n,i.consecutiveFailures=0,i.lastError=null,i.isRetrying=!1,i.health==="failed"||i.health==="unknown"?(i.health="healthy",this.notifyRecovery(e,r,"healthy","Sensor recovered successfully")):i.health==="degraded"&&(i.health="healthy",this.notifyRecovery(e,r,"healthy","Sensor returned to healthy state"));else if(i.lastFailureTimestamp=n,i.consecutiveFailures++,i.totalFailures++,i.lastError=a||"Unknown error",i.consecutiveFailures>=3){if(i.health!=="failed"){const o=i.health;i.health="failed",this.notifyRecovery(e,o,"failed",`Sensor failed after ${i.consecutiveFailures} consecutive failures`)}}else i.consecutiveFailures>=1&&(i.health==="healthy"||i.health==="unknown")&&(i.health="degraded",this.notifyRecovery(e,r,"degraded",`Sensor degraded after ${i.consecutiveFailures} consecutive failures`));this.sensorStatuses.set(e,i)}notifyRecovery(e,t,a,i){const n={sensorType:e,previousStatus:t,newStatus:a,timestamp:Date.now(),message:i};this.recoveryCallbacks.forEach(r=>{try{r(n)}catch(o){this.logger.error("Recovery callback error",{error:o})}})}logFailure(e,t,a,i){const n={sensorType:e,timestamp:Date.now(),error:t,retryAttempt:a,willRetry:i};this.failureLog.push(n),this.failureLog.length>100&&(this.failureLog=this.failureLog.slice(-100))}storeLastKnownGood(e,t){this.lastKnownGood.set(e,{...this.lastKnownGood.get(e),...t})}async retrySensorOperation(e,t,a="sensor operation"){let i=null,n=this.retryConfig.initialDelayMs;for(let o=0;o<=this.retryConfig.maxRetries;o++)try{if(o>0){const c=this.sensorStatuses.get(e);c&&(c.isRetrying=!0,this.sensorStatuses.set(e,c))}const l=await t();return this.updateSensorStatus(e,!0),l}catch(l){i=l;const c=i.message||String(l),u=o<this.retryConfig.maxRetries;this.logFailure(e,c,o,u),this.updateSensorStatus(e,!1,c),u&&(await this.delay(n),n=Math.min(n*this.retryConfig.backoffMultiplier,this.retryConfig.maxDelayMs))}const r=this.sensorStatuses.get(e);return r&&(r.isRetrying=!1,this.sensorStatuses.set(e,r)),null}delay(e){return new Promise(t=>setTimeout(t,e))}onSensorRecovery(e){return this.recoveryCallbacks.add(e),()=>this.recoveryCallbacks.delete(e)}getSensorStatus(e){return this.sensorStatuses.get(e)||null}getAllSensorStatuses(){return Array.from(this.sensorStatuses.values())}getFailureLog(e,t){let a=[...this.failureLog];return e&&(a=a.filter(i=>i.sensorType===e)),a.reverse(),t&&(a=a.slice(0,t)),a}getLastKnownGood(e){return this.lastKnownGood.get(e)||null}clearFailureLog(){this.failureLog=[]}updateRetryConfig(e){this.retryConfig={...this.retryConfig,...e}}async requestPermissions(e){const t=[];for(const a of e){let i=!1;switch(a){case"geolocation":i=await this.requestGeolocationPermission();break;case"motion":i=await this.requestMotionPermission();break;case"weather":i=!0;break}this.permissions.set(a,i),t.push({type:a,granted:i,timestamp:Date.now()})}return t}startMonitoring(e){this.permissions.get("motion")&&this.motion.startMonitoring(t=>{this.context.motion=t,this.context.timestamp=Date.now(),this.storeLastKnownGood("motion",{motion:t}),this.updateSensorStatus("motion",!0),e&&e(this.context)}),this.updateSnapshot()}stopMonitoring(){this.motion.stopMonitoring()}async updateSnapshot(){if(this.permissions.get("geolocation")){const e=await this.retrySensorOperation("geolocation",()=>this.geolocation.getCurrentPosition(),"getCurrentPosition");if(e){this.context.geolocation=e,this.storeLastKnownGood("geolocation",{geolocation:e});const t=this.geolocation.getBiome(e.latitude,e.longitude,e.altitude);this.context.biome=t}else{const t=this.getLastKnownGood("geolocation");t?.geolocation&&(this.context.geolocation=t.geolocation)}if((this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation)&&this.permissions.get("weather")){const t=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;if(t){const a=await this.retrySensorOperation("weather",()=>this.weather.getWeather(t.latitude,t.longitude),"getWeather");if(a)this.context.weather=a,this.storeLastKnownGood("weather",{weather:a});else{const i=this.getLastKnownGood("weather");i?.weather&&(this.context.weather=i.weather)}}}}return this.context.timestamp=Date.now(),this.context}calculateXPModifier(){let e=1;const t=this.context.motion||this.getLastKnownGood("motion")?.motion;if(t){const n=this.motion.detectActivity(t);n==="running"?e+=this.xpConfig.runningBonus:n==="walking"&&(e+=this.xpConfig.walkingBonus)}const a=this.context.weather||this.getLastKnownGood("weather")?.weather;if(a){const n=a.weatherType.toLowerCase();(n.includes("rain")||n.includes("storm"))&&(e+=this.xpConfig.stormBonus),n.includes("snow")&&(e+=this.xpConfig.snowBonus),a.isNight&&(e+=this.xpConfig.nightBonus)}const i=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;return i&&i.altitude&&i.altitude>this.xpConfig.altitudeThreshold&&(e+=this.xpConfig.altitudeBonus),Math.min(e,this.xpConfig.maxModifier)}getXpModifierBreakdown(){const e=[],t=this.context.motion||this.getLastKnownGood("motion")?.motion,a=t?this.motion.detectActivity(t):null;e.push({id:"running",label:"Running",icon:"🏃",bonus:this.xpConfig.runningBonus,active:a==="running"}),e.push({id:"walking",label:"Walking",icon:"🚶",bonus:this.xpConfig.walkingBonus,active:a==="walking"});const i=this.context.weather||this.getLastKnownGood("weather")?.weather,n=i?.weatherType?.toLowerCase()||"";e.push({id:"storm",label:"Stormy Weather",icon:"🌧️",bonus:this.xpConfig.stormBonus,active:n.includes("rain")||n.includes("storm")}),e.push({id:"snow",label:"Snowy Weather",icon:"❄️",bonus:this.xpConfig.snowBonus,active:n.includes("snow")}),e.push({id:"night",label:"Night Time",icon:"🌙",bonus:this.xpConfig.nightBonus,active:i?.isNight??!1});const r=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation,o=r?.altitude&&r.altitude>this.xpConfig.altitudeThreshold;e.push({id:"altitude",label:"High Altitude",icon:"⛰️",bonus:this.xpConfig.altitudeBonus,active:!!o});const l=e.filter(u=>u.active);return{total:Math.min(1+l.reduce((u,d)=>u+d.bonus,0),this.xpConfig.maxModifier),baseValue:1,sources:e,activeBonuses:l}}async calculateXPModifierWithForecast(e=12){let t=this.calculateXPModifier();const a=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;if(a&&this.permissions.get("weather")){const i=await this.retrySensorOperation("weather",()=>this.weather.getUpcomingWeather(a.latitude,a.longitude,e),"getUpcomingWeather");if(i){const n=i.worstWeatherType.toLowerCase();n.includes("thunderstorm")||n.includes("tornado")?t+=.15:(n.includes("snow")&&i.snowProbability>.5||n.includes("rain")&&i.rainProbability>.7)&&(t+=.1),n==="clear"&&!i.willRain&&!i.willSnow&&(t+=.05)}}return Math.min(t,this.xpConfig.maxModifier)}async calculateXPModifierWithSevereWeather(){let e=this.calculateXPModifier(),t=null;const a=this.context.weather||this.getLastKnownGood("weather")?.weather;a&&(t=this.weather.detectSevereWeather(a),t&&(e+=t.xpBonus));const i=Math.min(e,this.xpConfig.maxModifier),n=t?this.weather.getSafetyWarning(t):null;return{modifier:i,severeWeatherAlert:t,safetyWarning:n}}detectSevereWeather(){const e=this.context.weather||this.getLastKnownGood("weather")?.weather;return e?this.weather.detectSevereWeather(e):null}getSevereWeatherWarning(){const e=this.detectSevereWeather();return e?this.weather.getSafetyWarning(e):null}getPermissions(){return Array.from(this.permissions.entries()).map(([e,t])=>({type:e,granted:t,timestamp:Date.now()}))}checkAvailability(e){if(typeof window>"u")return!1;switch(e){case"geolocation":return"geolocation"in navigator;case"motion":return"DeviceMotionEvent"in window;case"weather":return!0;default:return!1}}async requestGeolocationPermission(){if(!this.checkAvailability("geolocation"))return!1;try{return await this.geolocation.getCurrentPosition(),!0}catch{return!1}}async requestMotionPermission(){if(!this.checkAvailability("motion"))return!1;if(typeof DeviceMotionEvent.requestPermission=="function")try{return await DeviceMotionEvent.requestPermission()==="granted"}catch{return!1}return!0}getCurrentActivity(){if(!this.permissions.get("motion"))return"unknown";const e=this.context.motion||this.getLastKnownGood("motion")?.motion;return e?this.motion.detectActivity(e):"unknown"}getDiagnostics(){const e=z.Logger.isDiagnosticMode();return{timestamp:Date.now(),diagnosticMode:e,sensors:Array.from(this.sensorStatuses.values()).map(t=>({type:t.type,status:t,permission:this.permissions.get(t.type)||!1,availability:this.checkAvailability(t.type),lastKnownGood:this.getLastKnownGood(t.type)})),cache:{geolocation:{age:this.geolocation.getCacheAge(),isExpired:this.geolocation.isCacheExpired(),stats:this.geolocation.getCacheStats()},weather:{size:this.weather.getCacheSize(),stats:this.weather.getCacheStats(),ttl:720}},performance:{weatherApi:this.weather.getWeatherApiStatistics(),forecastApi:this.weather.getForecastApiStatistics()},recentFailures:this.getFailureLog(void 0,10),permissions:this.getPermissions(),context:{hasGeolocation:!!this.context.geolocation,hasMotion:!!this.context.motion,hasWeather:!!this.context.weather,hasBiome:!!this.context.biome,timestamp:this.context.timestamp}}}enableDiagnosticMode(){z.Logger.enableDiagnosticMode(),this.logger.info("Diagnostic mode enabled")}disableDiagnosticMode(){z.Logger.disableDiagnosticMode(),this.logger.info("Diagnostic mode disabled")}printDashboard(e){li.displayEnvironmentalDiagnostics(this.getDiagnostics(),e)}getSolarInfo(e){const t=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;return t?this.weather.getSolarInfo(t.latitude,t.longitude,e):null}getSolarInfoForCoords(e,t,a){return this.weather.getSolarInfo(e,t,a)}}class np{constructor(e=""){this.baseUrl="https://api.steampowered.com",this.logger=z.Logger.for("SteamAPIClient"),this.currentGameApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.metadataApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentCurrentGameTimes=[],this.recentMetadataTimes=[],this.maxRecentSamples=100,this.apiKey=e}calculatePercentile(e,t){if(e.length===0)return 0;const a=[...e].sort((n,r)=>n-r),i=Math.ceil(t/100*a.length)-1;return a[Math.max(0,i)]}recordCurrentGameApiCall(e,t){const a=Date.now();t?(this.currentGameApiMetrics.successCount++,this.currentGameApiMetrics.totalTime+=e,this.currentGameApiMetrics.minTime=Math.min(this.currentGameApiMetrics.minTime,e),this.currentGameApiMetrics.maxTime=Math.max(this.currentGameApiMetrics.maxTime,e),this.recentCurrentGameTimes.push(e),this.recentCurrentGameTimes.length>this.maxRecentSamples&&this.recentCurrentGameTimes.shift()):this.currentGameApiMetrics.errorCount++,this.currentGameApiMetrics.lastCallTimestamp=a}recordMetadataApiCall(e,t){const a=Date.now();t?(this.metadataApiMetrics.successCount++,this.metadataApiMetrics.totalTime+=e,this.metadataApiMetrics.minTime=Math.min(this.metadataApiMetrics.minTime,e),this.metadataApiMetrics.maxTime=Math.max(this.metadataApiMetrics.maxTime,e),this.recentMetadataTimes.push(e),this.recentMetadataTimes.length>this.maxRecentSamples&&this.recentMetadataTimes.shift()):this.metadataApiMetrics.errorCount++,this.metadataApiMetrics.lastCallTimestamp=a}getCurrentGameApiMetrics(){return{...this.currentGameApiMetrics}}getCurrentGameApiStatistics(){const e=this.currentGameApiMetrics.successCount+this.currentGameApiMetrics.errorCount,t=this.currentGameApiMetrics.successCount>0?this.currentGameApiMetrics.totalTime/this.currentGameApiMetrics.successCount:0,a=e>0?this.currentGameApiMetrics.successCount/e*100:0;return{average:Math.round(t),min:this.currentGameApiMetrics.minTime===1/0?0:Math.round(this.currentGameApiMetrics.minTime),max:Math.round(this.currentGameApiMetrics.maxTime),totalCalls:e,successRate:Math.round(a*10)/10,p95:Math.round(this.calculatePercentile(this.recentCurrentGameTimes,95)),p99:Math.round(this.calculatePercentile(this.recentCurrentGameTimes,99))}}getMetadataApiMetrics(){return{...this.metadataApiMetrics}}getMetadataApiStatistics(){const e=this.metadataApiMetrics.successCount+this.metadataApiMetrics.errorCount,t=this.metadataApiMetrics.successCount>0?this.metadataApiMetrics.totalTime/this.metadataApiMetrics.successCount:0,a=e>0?this.metadataApiMetrics.successCount/e*100:0;return{average:Math.round(t),min:this.metadataApiMetrics.minTime===1/0?0:Math.round(this.metadataApiMetrics.minTime),max:Math.round(this.metadataApiMetrics.maxTime),totalCalls:e,successRate:Math.round(a*10)/10,p95:Math.round(this.calculatePercentile(this.recentMetadataTimes,95)),p99:Math.round(this.calculatePercentile(this.recentMetadataTimes,99))}}resetPerformanceMetrics(){this.currentGameApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.metadataApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentCurrentGameTimes=[],this.recentMetadataTimes=[]}async getCurrentGame(e){if(!this.apiKey)return this.logger.warn("Steam API key not provided"),null;const t=performance.now();let a=!1;try{const i=`${this.baseUrl}/ISteamUser/GetPlayerSummaries/v2/?key=${this.apiKey}&steamids=${e}`,n=await fetch(i);if(!n.ok)throw new Error(`Steam API error: ${n.statusText}`);const o=(await n.json()).response?.players?.[0];return o?.gameextrainfo?(a=!0,{name:o.gameextrainfo,appId:parseInt(o.gameid,10),source:"steam"}):null}catch(i){return this.logger.error("Failed to fetch current Steam game",{error:i}),null}finally{const i=performance.now()-t;this.recordCurrentGameApiCall(i,a)}}async getGameMetadata(e){const t=performance.now();let a=!1;try{const i=await this.findAppId(e);if(!i)return{name:e};const n=`https://steamcommunity.com/api/appdetails?appids=${i}`,r=await fetch(n);if(!r.ok)return{name:e,appId:i};const l=(await r.json())[i]?.data;return l?(a=!0,{appId:i,name:l.name||e,genre:l.genres?.map(c=>c.description)||[],description:l.short_description}):{name:e,appId:i}}catch(i){return this.logger.error(`Failed to fetch Steam metadata for ${e}`,{error:i}),{name:e}}finally{const i=performance.now()-t;this.recordMetadataApiCall(i,a)}}async findAppId(e){if(!this.apiKey)return null;try{const t=`${this.baseUrl}/ISteamApps/GetAppList/v2/`,a=await fetch(t);if(!a.ok)return null;const n=(await a.json()).applist?.apps||[],r=e.toLowerCase();return n.find(l=>l.name.toLowerCase()===r)?.appid||null}catch(t){return this.logger.error("Failed to find Steam app ID",{error:t}),null}}async getGameSchema(e){if(!this.apiKey)return null;try{const t=`${this.baseUrl}/ISteamUserStats/GetSchemaForGame/v2/?appid=${e}&key=${this.apiKey}`,a=await fetch(t);return a.ok&&(await a.json()).game||null}catch(t){return this.logger.error("Failed to fetch game schema",{error:t}),null}}}class rp{constructor(e={}){this.pollIntervalMs=6e4,this.pollingInterval=null,this.exponentialBackoff=1,this.maxBackoffMs=6e5,this.logger=z.Logger.for("GamingPlatformSensors"),this.xpConfig={maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},this.gamingContext={isActivelyGaming:!1,platformSource:"none",totalGamingMinutes:0,gamesPlayedWhileListening:[],lastUpdated:Date.now()},this.contextCallback=null,this.gameMetadataCache=new Map,this.cacheExpiryMs=864e5,"steam"in e&&e.steam&&(this.steamApiKey=e.steam.apiKey,this.steamUserId=e.steam.steamId,e.steam.pollInterval&&(this.pollIntervalMs=e.steam.pollInterval)),"metadataCacheExpiry"in e&&e.metadataCacheExpiry&&(this.cacheExpiryMs=e.metadataCacheExpiry),"maxBackoffMs"in e&&e.maxBackoffMs&&(this.maxBackoffMs=e.maxBackoffMs),"xpModifier"in e&&e.xpModifier&&(this.xpConfig={...this.xpConfig,...e.xpModifier}),this.steam=new np(this.steamApiKey)}async authenticate(e){return e?(this.steamUserId=e,!0):!!this.steamUserId}startMonitoring(e){this.pollingInterval||(this.contextCallback=e||null,this.updateGamingStatus(),this.pollingInterval=setInterval(()=>{this.updateGamingStatus()},this.pollIntervalMs))}stopMonitoring(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=null),this.contextCallback=null}async updateGamingStatus(){try{const e=this.steamUserId?await this.steam.getCurrentGame(this.steamUserId):null;this.gamingContext={isActivelyGaming:!!e,platformSource:e?"steam":"none",currentGame:e?{name:e.name,source:e.source,genre:await this.getGameMetadata(e.name).then(t=>t?.genre),sessionDuration:e.sessionDuration,partySize:"partySize"in e&&typeof e.partySize=="number"?e.partySize:void 0}:void 0,totalGamingMinutes:this.gamingContext.totalGamingMinutes,gamesPlayedWhileListening:this.gamingContext.gamesPlayedWhileListening,lastUpdated:Date.now()},this.exponentialBackoff=1,this.contextCallback&&this.contextCallback(this.gamingContext)}catch(e){this.logger.warn("Error updating gaming status",{error:e}),this.applyExponentialBackoff()}}applyExponentialBackoff(){this.exponentialBackoff=Math.min(this.exponentialBackoff*2,this.maxBackoffMs/this.pollIntervalMs),this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=setInterval(()=>this.updateGamingStatus(),this.pollIntervalMs*this.exponentialBackoff))}async getGameMetadata(e){const t=this.gameMetadataCache.get(e);if(t&&Date.now()-t.lastUpdated<this.cacheExpiryMs)return t;try{const a=await this.steam.getGameMetadata(e);return this.gameMetadataCache.set(e,{genre:a?.genre,lastUpdated:Date.now()}),a}catch(a){return this.logger.warn(`Failed to fetch metadata for game: ${e}`,{error:a}),null}}isPlayingGame(e){const t=this.getContext();return t.currentGame?t.currentGame.name.toLowerCase()===e.toLowerCase():!1}calculateGamingBonus(){const e=this.getContext();if(!e.isActivelyGaming)return 1;let t=this.xpConfig.gamingBaseBonus;const a=e.currentGame;if(a){if(a.genre)for(const i of a.genre){const n=i.toLowerCase();n.includes("rpg")?t+=this.xpConfig.gamingRPGBonus:n.includes("action")?t+=.15:n.includes("strategy")&&(t+=.1)}if(a.partySize&&a.partySize>1&&(t+=this.xpConfig.gamingMultiplayerBonus),a.sessionDuration){const i=a.sessionDuration/60,n=Math.min(i/4*.2,.2);t+=n}}return Math.min(1+t,this.xpConfig.maxGamingModifier)}getContext(){return{...this.gamingContext}}recordGameSession(e,t){this.gamingContext.gamesPlayedWhileListening.includes(e)||this.gamingContext.gamesPlayedWhileListening.push(e),this.gamingContext.totalGamingMinutes+=t}getDiagnostics(){return{timestamp:Date.now(),steam:{isAuthenticated:!!this.steamUserId,userId:this.steamUserId,apiKey:!!this.steamApiKey},gamingContext:this.getContext(),polling:{isActive:this.pollingInterval!==null,intervalMs:this.pollIntervalMs,exponentialBackoff:this.exponentialBackoff},cache:{gameMetadataCacheSize:this.gameMetadataCache.size,cachedGames:Array.from(this.gameMetadataCache.keys())},performance:{currentGameApi:this.steam.getCurrentGameApiStatistics(),metadataApi:this.steam.getMetadataApiStatistics()}}}async fetchGameSchema(e){return this.steam.getGameSchema(e)}printDashboard(e){li.displayGamingDiagnostics(this.getDiagnostics(),e)}}const Jn={geolocation:{cacheTTL:300*1e3,useLocalStorage:!0,enableHighAccuracy:!0,timeout:5e3},weather:{apiKey:"",cacheTTL:720*1e3,forecastCacheTTL:3600*1e3,useLocalStorage:!0},gaming:{steam:{apiKey:"",steamId:void 0,pollInterval:6e4},metadataCacheExpiry:1440*60*1e3,maxBackoffMs:600*1e3},xpModifier:{maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},retry:{enabled:!0,maxRetries:3,initialDelayMs:1e3,maxDelayMs:1e4,backoffMultiplier:2}};function Qn(){const s={};if(typeof process<"u"&&process.env?.WEATHER_API_KEY&&(s.weather={...s.weather,apiKey:process.env.WEATHER_API_KEY}),typeof process<"u"&&process.env?.STEAM_API_KEY&&(s.gaming={...s.gaming,steam:{...s.gaming?.steam,apiKey:process.env.STEAM_API_KEY}}),typeof process<"u"&&process.env?.STEAM_USER_ID&&(s.gaming={...s.gaming,steam:{...s.gaming?.steam,steamId:process.env.STEAM_USER_ID}}),typeof process<"u"&&process.env?.XP_MAX_MODIFIER){const e=parseFloat(process.env.XP_MAX_MODIFIER);!isNaN(e)&&e>0&&(s.xpModifier={...s.xpModifier,maxModifier:e})}return s}function op(s={}){const e=Qn();return Aa(Jn,Aa(e,s))}function Aa(s,e){const t={...s};for(const a in e){const i=e[a],n=t[a];i!==void 0&&(typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof n=="object"&&n!==null&&!Array.isArray(n)?t[a]=Aa(n,i):t[a]=i)}return t}const lp=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],Ue={xp:{level_thresholds:lp,xp_per_second:1,xp_per_track_completion:50,activity_bonuses:{stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5},track_mastery_threshold:10,mastery_bonus_xp:100,rhythmXP:Me},statIncrease:{maxStatCap:20,strategy:"dnD5e",autoApply:!0,statIncreaseLevels:[4,8,12,16,19]},levelUp:{useAverageHP:!1,allowManualStatSelection:!0,showNotifications:!0}};function cp(s){return s?{xp:{...Ue.xp,...s.xp,activity_bonuses:{...Ue.xp.activity_bonuses,...s.xp?.activity_bonuses},rhythmXP:Pt(s.xp?.rhythmXP)},statIncrease:{...Ue.statIncrease,...s.statIncrease},levelUp:{...Ue.levelUp,...s.levelUp}}:Ue}const tt=[{name:"Flame Tongue",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse"],description:"A magic sword that can burst into flame, dealing extra fire damage and shedding light. The wielder can ignite or extinguish the flames as a bonus action.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light in a 20ft radius and dim light for 20ft beyond"}],grantsFeatures:[{id:"flame_tongue_ignition",name:"Ignition",description:"As a bonus action, you can speak the command word to cause the sword to burst into flame. The flames last until you speak the command word again or until you drop or sheathe the sword.",effects:[{type:"special_property",target:"fire_illumination",value:!0,description:"Sword bursts into flame"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","fire","weapon","versatile"]},{name:"Vorpal Sword",type:"weapon",rarity:"legendary",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A legendary weapon that decapitates creatures on a natural 20. One of the most powerful weapons in existence, capable of instantly slaying even the mightiest foes.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls"},{type:"special_property",target:"decapitation",value:"natural_20",description:"On a roll of 20, decapitate creatures with heads",condition:{type:"on_hit",value:!0}}],spawnWeight:0,source:"custom",tags:["magic","weapon","legendary","versatile","artifact"]},{name:"Frost Brand",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"cold",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A freezing cold sword that deals extra cold damage and grants its wielder resistance to fire. Can extinguish non-magical flames with a mere thought.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage on hit"},{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"},{type:"special_property",target:"extinguish_flames",value:!0,description:"Extinguish all non-magical flames within 30ft"}],spawnWeight:.1,source:"custom",tags:["magic","cold","weapon","fire_resistance"]},{name:"Dragonslayer Longsword",type:"weapon",rarity:"very_rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A magical longsword enchanted against dragonkind. Deals devastating extra damage to dragons and glows in the presence of these ancient creatures.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"dragon_damage",value:"2d6",description:"+2d6 damage against dragons",condition:{type:"vs_creature_type",value:"dragon"}}],spawnWeight:.05,source:"custom",tags:["magic","weapon","versatile","dragon_slaying"]},{name:"Lightning Lance",type:"weapon",rarity:"rare",weight:6,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["reach","two-handed"],description:"A spear crackling with electrical energy that extends your reach. Deals extra lightning damage on hits and can arc to nearby enemies.",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage on hit"},{type:"special_property",target:"lightning_arc",value:"half_damage",description:"On critical hit, lightning arcs to another creature within 10ft for half damage"}],spawnWeight:.1,source:"custom",tags:["magic","weapon","lightning","reach"]},{name:"Mithral Shirt",type:"armor",rarity:"uncommon",weight:10,acBonus:12,description:"Finely crafted armor made of mithral, a light and incredibly strong metal. Counts as light armor for all class features and imposes no disadvantage on Stealth.",properties:[{type:"passive_modifier",target:"ac",value:12,description:"Base AC: 12 + DEX (max 2)"},{type:"special_property",target:"armor_classification",value:"light",description:"Counts as light armor for class features"},{type:"special_property",target:"stealth_normal",value:!0,description:"No stealth disadvantage"}],spawnWeight:.2,source:"custom",tags:["magic","armor","light","mithral"]},{name:"+1 Chain Shirt",type:"armor",rarity:"rare",weight:20,acBonus:14,description:"Magically reinforced chain armor that provides improved protection without adding extra weight. The enchanted metal links deflect blows more effectively than ordinary chain.",properties:[{type:"passive_modifier",target:"ac",value:14,description:"Fixed AC: 14 (13 base + 1 enhancement)"}],spawnWeight:.15,source:"custom",tags:["magic","armor","light","enhanced"]},{name:"+2 Chain Shirt",type:"armor",rarity:"very_rare",weight:20,acBonus:15,description:"Superiorly enchanted chain armor with powerful protective magic. The links are magically hardened to provide exceptional defense while remaining flexible.",properties:[{type:"passive_modifier",target:"ac",value:15,description:"Fixed AC: 15 (13 base + 2 enhancement)"}],spawnWeight:.1,source:"custom",tags:["magic","armor","light","enhanced"]},{name:"+1 Plate Armor",type:"armor",rarity:"rare",weight:65,acBonus:19,description:"Magically enhanced plate armor that provides superior protection. The enchantment improves the armor's protective qualities while maintaining its strength requirement.",properties:[{type:"passive_modifier",target:"ac",value:19,description:"Fixed AC: 19 (18 base + 1 enhancement)"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:15,description:"Requires STR 15"}],spawnWeight:.1,source:"custom",tags:["magic","armor","heavy","enhanced"]},{name:"Elven Chain",type:"armor",rarity:"rare",weight:20,acBonus:16,description:"Exquisitely crafted elven armor that functions as light armor for all purposes. Can be worn even by those without armor proficiency and makes no sound when moved.",properties:[{type:"passive_modifier",target:"ac",value:16,description:"Fixed AC: 16"},{type:"special_property",target:"armor_classification",value:"light",description:"Counts as light armor for all features"},{type:"special_property",target:"stealth_normal",value:!0,description:"No stealth disadvantage"},{type:"special_property",target:"no_proficiency_required",value:!0,description:"Can be worn without armor proficiency"}],spawnWeight:.05,source:"custom",tags:["magic","armor","elven","versatile"]},{name:"Belt of Giant Strength (Hill Giant)",type:"item",rarity:"rare",weight:1,description:"A belt woven from giant hair that grants the strength of a hill giant. While worn, your Strength score becomes 21, regardless of your actual strength.",properties:[{type:"stat_bonus",target:"STR",value:21,description:"Strength score becomes 21 while worn",condition:{type:"while_equipped",value:!0},stackable:!1}],spawnWeight:.15,source:"custom",tags:["magic","wondrous","strength"]},{name:"Amulet of Proof Against Detection",type:"item",rarity:"uncommon",weight:.1,description:"A protective amulet that hides you from divination magic. Makes you difficult to detect through magical means and grants some protection against spells.",properties:[{type:"ability_unlock",target:"anti_magic_detection",value:!0,description:"Hidden from divination magic"},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to saving throws against spells"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","anti_detection"]},{name:"Headband of Intellect",type:"item",rarity:"uncommon",weight:.1,description:"A forehead band embroidered with intellectual symbols. While worn, your Intelligence score becomes 19, making you brilliant regardless of your natural intellect.",properties:[{type:"stat_bonus",target:"INT",value:19,description:"Intelligence score becomes 19 while worn",stackable:!1}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","intelligence"]},{name:"Boots of Elvenkind",type:"item",rarity:"uncommon",weight:1,description:"Silent magical boots that make you an expert at stealth. Your steps make no sound, and you gain advantage or bonuses to Stealth checks.",grantsSkills:[{skillId:"stealth",level:"expertise"}],properties:[{type:"special_property",target:"silent_steps",value:!0,description:"Make no noise when walking"},{type:"passive_modifier",target:"stealth_check",value:1,description:"+1 to Stealth checks"}],spawnWeight:.3,source:"custom",tags:["magic","wondrous","boots","stealth","elven"]},{name:"Gloves of Thievery",type:"item",rarity:"uncommon",weight:.1,description:"Magical gloves that make you a master of larceny. Grants expertise with thieves' tools and proficiency in sleight of hand.",grantsSkills:[{skillId:"sleight_of_hand",level:"proficient"}],properties:[{type:"passive_modifier",target:"sleight_of_hand",value:2,description:"+2 to Sleight of Hand checks"},{type:"special_property",target:"thieves_tools_expertise",value:!0,description:"Advantage on checks using thieves' tools"}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","thieves_tools"]},{name:"Boots of Speed",type:"item",rarity:"rare",weight:1,description:"Magical boots that dramatically increase your speed and grant freedom of movement. You can dash as a bonus action and leave no trace when you walk.",properties:[{type:"passive_modifier",target:"speed",value:10,description:"+10 walking speed"}],grantsFeatures:[{id:"boots_of_speed_dash",name:"Speed Burst",description:"You can take the Dash action as a bonus action, and moving leaves no trace.",effects:[{type:"special_property",target:"bonus_action_dash",value:!0,description:"Dash as bonus action"}],source:"equipment_inline"},{id:"boots_of_speed_freedom",name:"Unfettered Movement",description:"Your movement is unimpeded by difficult terrain, and spells and effects can neither reduce your speed nor cause you to be restrained.",effects:[{type:"special_property",target:"ignore_difficult_terrain",value:!0,description:"Ignore difficult terrain"},{type:"special_property",target:"freedom_of_movement",value:!0,description:"Immunity to speed reduction and restraint"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","wondrous","boots","speed"]},{name:"Boots of Striding and Springing",type:"item",rarity:"uncommon",weight:1,description:"Magical boots that enhance your mobility. Move faster, jump farther, and require less running start for long and high jumps.",properties:[{type:"passive_modifier",target:"walking_speed",value:10,description:"+10 walking speed"},{type:"passive_modifier",target:"jump_distance",value:3,description:"Triple jump distance"},{type:"ability_unlock",target:"standing_long_jump",value:!0,description:"Can run, long jump, and high jump with only 10 feet of running start"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","boots","movement","jumping"]},{name:"Boots of Flying",type:"item",rarity:"rare",weight:1,description:"Magical boots that grant the power of flight. You can fly at will as a bonus action, though overuse temporarily exhausts their magic.",properties:[{type:"ability_unlock",target:"fly_speed",value:60,description:"Fly at 60 feet per round"}],grantsFeatures:[{id:"boots_of_flying_power",name:"Flight",description:"You can fly as a bonus action. The boots lose this power for 12 hours if you use it for more than 4 hours in a 24 hour period.",effects:[{type:"ability_unlock",target:"fly_speed",value:60,description:"Fly at 60 feet"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","wondrous","boots","flight"]},{name:"Ring of Protection",type:"item",rarity:"rare",weight:.1,description:"A protective ring that enhances your defense. Grants a bonus to Armor Class and all saving throws, stacking with other protective items.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to all saving throws",stackable:!0}],spawnWeight:.2,source:"custom",tags:["magic","ring","defense","ac","saves"]},{name:"Amulet of Proof Against Poison",type:"item",rarity:"uncommon",weight:.1,description:"A protective amulet that grants complete immunity to poison. Protects against both poison damage and the poisoned condition.",properties:[{type:"passive_modifier",target:"immunity_poison",value:!0,description:"Immunity to poison damage"},{type:"special_property",target:"condition_immunity",value:"poisoned",description:"Immune to poisoned condition"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","poison_immunity","defense"]},{name:"Cloak of Protection",type:"item",rarity:"uncommon",weight:1,description:"A magical cloak that wraps you in protective energy. Grants bonuses to Armor Class and saving throws that stack with rings of protection.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to all saving throws",stackable:!0}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","cloak","defense","ac","saves"]},{name:"Goggles of Night",type:"item",rarity:"uncommon",weight:.1,description:"Magical lenses that grant darkvision. You can see in darkness as if it were dim light, out to a range of 60 feet.",properties:[{type:"ability_unlock",target:"darkvision",value:60,description:"Darkvision 60 feet"}],spawnWeight:.3,source:"custom",tags:["magic","wondrous","vision","darkvision"]},{name:"Lantern of Revealing",type:"item",rarity:"uncommon",weight:2,description:"A magical lantern that reveals invisible creatures and objects. Sheds light in a 60-foot radius and exposes anything hidden by illusion magic.",properties:[{type:"special_property",target:"reveal_invisible",value:30,description:"Reveal invisible creatures and objects within 30ft"},{type:"special_property",target:"light",value:"bright_light_30ft_dim_30ft",description:"Sheds bright light 30ft, dim light 30ft beyond"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","light","detection"]},{name:"Ring of Spell Storing",type:"item",rarity:"rare",weight:.1,description:"A magical ring that can store spells for later use. The caster places spells into the ring, and any creature can cast them regardless of class.",properties:[{type:"special_property",target:"spell_storage",value:5,description:"Store up to 5 levels of spells"}],grantsSpells:[{spellId:"stored_spell",level:1,uses:1,recharge:"short_rest"}],spawnWeight:.15,source:"custom",tags:["magic","ring","spell_storage"]},{name:"Pearl of Power (3rd Level)",type:"item",rarity:"uncommon",weight:.1,description:"A small pearl that glitters with magical energy. Once per day, you can use it to recover a spent 3rd level spell slot.",properties:[{type:"special_property",target:"spell_slot_recovery",value:3,description:"Recover one expended 3rd level spell slot",condition:{type:"custom",value:"once_per_day",description:"Once per day at dawn"}}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","spell_recovery","pearl"]},{name:"Wand of Magic Missiles",type:"item",rarity:"uncommon",weight:1,description:"A wand that fires unerring magic missiles. Contains 7 charges that refresh each dawn at sunrise.",properties:[{type:"special_property",target:"spell_charges",value:7,description:"7 charges, expends 1 per use"}],grantsSpells:[{spellId:"magic_missile",level:1,uses:7,recharge:"dawn"}],damage:{dice:"1d4+1",damageType:"force"},spawnWeight:.2,source:"custom",tags:["magic","wand","evocation","force"]},{name:"-1 Cursed Sword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A maliciously enchanted sword that appears magical but impairs your combat abilities. Once attuned, it cannot be removed without breaking the curse.",properties:[{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 penalty to attack and damage rolls"},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Once attuned, cannot remove unless targeted by remove curse"}],spawnWeight:.05,source:"custom",tags:["magic","weapon","cursed","curse","negative"]},{name:"Belt of Strength Drain (Cursed)",type:"item",rarity:"uncommon",weight:1,description:"A cursed belt that appears beneficial but actually drains your strength. Once donned, it cannot be removed until the curse is broken.",properties:[{type:"stat_bonus",target:"STR",value:-4,description:"-4 Strength while worn",stackable:!0},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Appears as Belt of Giant Strength until donned"}],spawnWeight:0,source:"custom",tags:["magic","cursed","curse","debuff"]},{name:"Helmet of Opposite Alignment (Cursed)",type:"item",rarity:"rare",weight:3,description:"A cursed helmet that slowly corrupts your alignment, turning you toward your moral opposite. Only detectable when it's too late.",properties:[{type:"special_property",target:"alignment_change",value:"opposite",description:"Changes character alignment to opposite",condition:{type:"on_damage_taken",value:!0}},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Must be attuned to remove, changes alignment"}],spawnWeight:0,source:"custom",tags:["magic","cursed","curse","alignment","helmet"]},{name:"Moon Sickle",type:"weapon",rarity:"rare",weight:2,damage:{dice:"1d4",damageType:"slashing",versatile:"1d6"},weaponProperties:["finesse","light","thrown"],description:"A druidic sickle blessed by moonlight. Deals extra radiant damage at night and gleams with silvery light when the moon is visible.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"1d6",description:"+1d6 radiant damage at night",condition:{type:"at_time_of_day",value:"night"}}],spawnWeight:.1,source:"custom",tags:["magic","weapon","finesse","light","moon","radiant"]},{name:"Sun Blade",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"radiant",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A brilliant blade that thrives in sunlight but weakens in darkness. Deals devastating radiant damage during the day but becomes clumsy at night.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"1d8",description:"+1d8 radiant damage in daylight",condition:{type:"at_time_of_day",value:"day"}},{type:"special_property",target:"light",value:"bright_light_20ft_dim_20ft",description:"Sheds bright light 20ft, dim 20ft"},{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 to attack rolls at night",condition:{type:"at_time_of_day",value:"night"}}],spawnWeight:.05,source:"custom",tags:["magic","weapon","sun","radiant","light"]},{name:"Dwarf-Forged Armor",type:"armor",rarity:"rare",weight:40,acBonus:15,description:"Magically crafted dwarven armor that only works properly for dwarf characters. Grants additional protection and saving throw bonuses to its intended users.",properties:[{type:"passive_modifier",target:"ac",value:15,description:"Base AC: 15 + DEX (max 2)"},{type:"passive_modifier",target:"ac",value:2,description:"+2 AC for dwarves only",condition:{type:"wielder_race",value:"Dwarf"}},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to saving throws for dwarves",condition:{type:"wielder_race",value:"Dwarf"}}],spawnWeight:.1,source:"custom",tags:["magic","armor","medium","dwarven"]},{name:"Wizard's Staff",type:"weapon",rarity:"uncommon",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile","two-handed"],description:"An arcane staff that enhances magical abilities for wizards. Grants bonuses to spell attacks and save DC, but only functions for wizards.",properties:[{type:"passive_modifier",target:"spell_attack",value:1,description:"+1 to spell attack rolls for wizards",condition:{type:"wielder_class",value:"Wizard"}},{type:"passive_modifier",target:"spell_save_dc",value:1,description:"+1 to spell save DC for wizards",condition:{type:"wielder_class",value:"Wizard"}}],spawnWeight:.2,source:"custom",tags:["magic","weapon","staff","wizard"]},{name:"Flaming Longsword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],templateId:"flaming_weapon_template",description:"A longsword enchanted with fiery magic. Burns with magical flame and sheds light when its power is activated.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft"}],spawnWeight:.15,source:"custom",tags:["magic","weapon","fire","flaming","template"]},{name:"Frost Longsword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"cold",versatile:"1d10"},weaponProperties:["finesse","versatile"],templateId:"frost_weapon_template",description:"A longsword enchanted with icy magic. Freezes with cold power and can extinguish flames when wielded.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],spawnWeight:.15,source:"custom",tags:["magic","weapon","cold","frost","template"]}],_={WEAPON_ENCHANTMENTS:{plusOne:{id:"plus_one",name:"+1 Enhancement",description:"A magical enhancement that improves the weapon's accuracy and striking power. The weapon becomes supernaturally sharp and well-balanced, striking true more often and cutting deeper.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusTwo:{id:"plus_two",name:"+2 Enhancement",description:"A potent magical enhancement that significantly improves the weapon's combat effectiveness. The weapon hums with arcane energy, seeking its target with unerring precision and delivering devastating blows.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusThree:{id:"plus_three",name:"+3 Enhancement",description:"An exceptionally powerful magical enhancement that transforms the weapon into an instrument of legend. The weapon pulses with visible arcane energy, striking with supernatural force and nearly flawless accuracy.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},flaming:{id:"flaming",name:"Flaming",description:"The weapon bursts into flame upon command, dealing additional fire damage and illuminating the surrounding area. The flames do not harm the wielder but sear foes with each strike. Ancient runes of fire glow along the blade when activated.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light in a 20ft radius and dim light for 20ft beyond"}],appliedAt:new Date().toISOString(),source:"enchantment"},frost:{id:"frost",name:"Frost",description:"The weapon is sheathed in supernatural cold, freezing whatever it touches. A pale white aura surrounds the blade, and each strike leaves frost on the target's wound, slowing them with supernatural chill.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},shocking:{id:"shocking",name:"Shocking",description:"Crackling arcs of lightning dance across this weapon, delivering painful electrical shocks to anyone it strikes. The air around the weapon hums with ozone, and blue sparks fly with each impact.",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},thundering:{id:"thundering",name:"Thundering",description:"Each strike from this weapon is accompanied by a deafening thunderclap that can be heard for hundreds of feet. The concussion from impacts can knock foes back and leave them disoriented from the deafening noise.",properties:[{type:"damage_bonus",target:"thunder_damage",value:"1d6",description:"+1d6 thunder damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"thunderous_strike",value:!0,description:"Creates a thunderous clap on hit"}],appliedAt:new Date().toISOString(),source:"enchantment"},acidic:{id:"acidic",name:"Acidic",description:"The weapon drips with corrosive acid that continues to eat away at armor and flesh after each strike. Dark green ichor coats the blade, hissing as it melts through defenses and continues burning the wound.",properties:[{type:"damage_bonus",target:"acid_damage",value:"1d6",description:"+1d6 acid damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},poison:{id:"poison",name:"Poisoned",description:"A sinister coating of supernatural poison coats this weapon, weakening foes with every wound. The poison is distilled from venomous creatures and dark alchemy, causing progressive weakness in those struck.",properties:[{type:"damage_bonus",target:"poison_damage",value:"1d6",description:"+1d6 poison damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},holy:{id:"holy",name:"Holy",description:"Consecrated by divine rites, this weapon glows with holy light that burns the unholy. Radiant energy swirls around the blade, dealing extra damage to creatures of darkness and evil while leaving the wielder untouched.",properties:[{type:"damage_bonus",target:"radiant_damage",value:"1d6",description:"+1d6 radiant damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},vampiric:{id:"vampiric",name:"Vampiric",description:"This cursed weapon draws vitality from those it strikes, transferring their life force to the wielder. Dark red veins pulse along the blade as it feeds, and the wielder feels a rush of healing energy with each killing blow.",properties:[{type:"special_property",target:"life_steal",value:"1d6",description:"Regain 1d6 HP when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},vorpalEdge:{id:"vorpal_edge",name:"Vorpal Edge",description:"The edge of this weapon is supernaturally sharp, capable of striking致命 wounds with remarkable frequency. The blade seems to seek vital points on its own, turning glancing blows into devastating critical hits.",properties:[{type:"special_property",target:"expanded_crit_range",value:19,description:"Critical hits on 19-20"}],appliedAt:new Date().toISOString(),source:"enchantment"},keenEdge:{id:"keen_edge",name:"Keen Edge",description:"This weapon has been honed to perfection by magical means, its edge so impossibly sharp that it can slip through the smallest gaps in armor. Every swing has a chance to strike a致命 point, making even near-misses into devastating hits.",properties:[{type:"special_property",target:"expanded_crit_range",value:18,description:"Critical hits on 18-20"}],appliedAt:new Date().toISOString(),source:"enchantment"},mighty:{id:"mighty",name:"Mighty",description:"Enchanted to strike with exceptional force, this weapon deals more damage with each blow. The weapon feels unnaturally heavy and solid in the hand, transferring more kinetic energy into each impact for crushing wounds.",properties:[{type:"special_property",target:"damage_die_increase",value:1,description:"Weapon damage dice increased by one step"}],appliedAt:new Date().toISOString(),source:"enchantment"},returning:{id:"returning",name:"Returning",description:"This thrown weapon magically returns to the wielder's hand immediately after each attack. An invisible tether connects weapon to wielder, pulling it back through the air regardless of obstacles or distance.",properties:[{type:"special_property",target:"returns_to_hand",value:!0,description:"Weapon returns to wielder's hand immediately after being thrown"}],appliedAt:new Date().toISOString(),source:"enchantment"},lifestealing:{id:"lifestealing",name:"Lifestealing",description:"A powerful variant of vampiric enchantment that drains significantly more vitality from victims. The weapon pulses with dark crimson energy that gushes forth with each strike, healing the wielder for substantial amounts.",properties:[{type:"special_property",target:"life_steal",value:"2d6",description:"Regain 2d6 HP when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"}},ARMOR_ENCHANTMENTS:{plusOne:{id:"plus_one_armor",name:"+1 Armor Enhancement",description:"A magical enhancement that makes the armor supernaturally resistant to blows without adding weight. The material seems to harden when struck, turning aside attacks that would have found purchase in mundane armor.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusTwo:{id:"plus_two_armor",name:"+2 Armor Enhancement",description:"A potent magical enhancement that significantly improves the armor's protective qualities. The armor gleams with protective enchantments, and blows that connect seem to strike an invisible barrier just beneath the surface.",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 Armor Class",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}},RESISTANCE_ENCHANTMENTS:{fire:{id:"fire_resistance",name:"Fire Resistance",description:"The wearer is protected from fire and heat, as if continually under the effects of a protection spell. Flames lick harmlessly at the armor, and the wearer can walk through infernos that would incinerate ordinary mortals.",properties:[{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},cold:{id:"cold_resistance",name:"Cold Resistance",description:"Magical warmth protects the wearer from the bitterest cold. Frost cannot form on the armor, and the wearer remains comfortable in temperatures that would freeze others solid, protected by an unseen aura of heat.",properties:[{type:"passive_modifier",target:"resistance_cold",value:!0,description:"Resistance to cold damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},lightning:{id:"lightning_resistance",name:"Lightning Resistance",description:"The armor conducts electricity harmlessly away from the wearer, dispersing lightning strikes across its surface. Lightning bolts crackle harmlessly across the enchanted metal, leaving the wearer untouched.",properties:[{type:"passive_modifier",target:"resistance_lightning",value:!0,description:"Resistance to lightning damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},acid:{id:"acid_resistance",name:"Acid Resistance",description:"The armor is protected from corrosion by potent magical wards. Acid sizzles harmlessly on the surface without eating through the metal, and the wearer is protected from splashes of corrosive substances.",properties:[{type:"passive_modifier",target:"resistance_acid",value:!0,description:"Resistance to acid damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},poison:{id:"poison_resistance",name:"Poison Resistance",description:"Magical cleansing properties protect the wearer from toxins and venoms. Poisonous fumes and coated weapons cannot harm the wearer, as the enchantment neutralizes poisons before they can take effect.",properties:[{type:"passive_modifier",target:"resistance_poison",value:!0,description:"Resistance to poison damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},necrotic:{id:"necrotic_resistance",name:"Necrotic Resistance",description:"Dark protective wards shield the wearer from the draining touch of undeath and necrotic energies. The armor glimmers faintly with opposing light that pushes back against death magic and life-draining attacks.",properties:[{type:"passive_modifier",target:"resistance_necrotic",value:!0,description:"Resistance to necrotic damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},radiant:{id:"radiant_resistance",name:"Radiant Resistance",description:"The armor can absorb and disperse divine and radiant energy without harm. Holy fire and celestial beams wash over the wearer like water, their blinding intensity reduced to manageable levels.",properties:[{type:"passive_modifier",target:"resistance_radiant",value:!0,description:"Resistance to radiant damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},thunder:{id:"thunder_resistance",name:"Thunder Resistance",description:"The wearer is protected from sonic damage and concussive force. Thunderous impacts and deafening blasts are dampened by the armor, which absorbs and disperses the energy harmlessly.",properties:[{type:"passive_modifier",target:"resistance_thunder",value:!0,description:"Resistance to thunder damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},all:{id:"all_resistance",name:"Universal Resistance",description:"An incredibly powerful enchantment that provides resistance to all forms of damage. The armor seems to exist partially out of phase with reality, causing attacks of all types to glance off or pass harmlessly through.",properties:[{type:"passive_modifier",target:"resistance_all",value:!0,description:"Resistance to all damage"}],appliedAt:new Date().toISOString(),source:"enchantment"}},COMBO_ENCHANTMENTS:{holyAvenger:{id:"holy_avenger",name:"Holy Avenger",description:"The ultimate weapon of a paladin, consecrated to destroy evil. This blade glows with blinding holy light when wielded by a righteous warrior, dealing devastating damage to fiends and undead while protecting the wielder from dark magic.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage vs fiends and undead",condition:{type:"vs_creature_type",value:"fiend"}},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage vs fiends and undead",condition:{type:"vs_creature_type",value:"undead"}},{type:"passive_modifier",target:"saving_throws",value:5,description:"+5 to saving throws vs spells"}],appliedAt:new Date().toISOString(),source:"enchantment"},dragonSlayer:{id:"dragon_slayer",name:"Dragon Slayer",description:"Forged specifically to hunt the greatest of beasts, this weapon pulses with dragon-slaying magic. Runes of ancient dragon hunters glow when chromatic or metallic dragons are near, and the blade strikes true against their armored scales.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls"},{type:"damage_bonus",target:"dragon_damage",value:"3d6",description:"+3d6 damage against dragons",condition:{type:"vs_creature_type",value:"dragon"}},{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},demonHunter:{id:"demon_hunter",name:"Demon Hunter",description:"Blessed by celestial powers for the specific purpose of hunting fiends. The weapon bears holy symbols that burn with cold fire when in the presence of demons, causing extra pain to creatures of the lower planes.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"fiend_damage",value:"2d6",description:"+2d6 damage against fiends",condition:{type:"vs_creature_type",value:"fiend"}}],appliedAt:new Date().toISOString(),source:"enchantment"},undeadBane:{id:"undead_bane",name:"Undead Bane",description:"Consecrated to disrupt the unnatural forces that animate the dead. The weapon glows with positive energy that burns the undead, and its touch can momentarily restore peace to tormented spirits even as it destroys their forms.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage against undead",condition:{type:"vs_creature_type",value:"undead"}}],appliedAt:new Date().toISOString(),source:"enchantment"}},CURSES:{minusOne:{id:"minus_one",name:"Cursed: -1 Penalty",description:"A malevolent enchantment that subtly sabotages the wielder's combat ability. The weapon feels slightly off-balance or slippery, causing blows to miss their mark or strike with reduced force despite the wielder's best efforts.",properties:[{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 penalty to attack and damage rolls"}],appliedAt:new Date().toISOString(),source:"curse"},minusTwo:{id:"minus_two",name:"Cursed: -2 Penalty",description:"A stronger version of the penalty curse that significantly impairs the wielder's fighting ability. The weapon actively fights against being used effectively, as if it has a will of its own that opposes its owner.",properties:[{type:"passive_modifier",target:"attack_roll",value:-2,description:"-2 penalty to attack and damage rolls"}],appliedAt:new Date().toISOString(),source:"curse"},weakness:{id:"weakness",name:"Cursed: Weakness",description:"The armor or weapon saps the wearer's physical strength, leaving them feeling constantly exhausted and feeble. Muscles seem to wither under its influence, and once-trivial feats of strength become impossibly difficult.",properties:[{type:"stat_bonus",target:"STR",value:-4,description:"-4 Strength while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},feeblemind:{id:"feeblemind",name:"Cursed: Feeblemind",description:"A maddening curse that clouds the mind and disrupts logical thought. The wearer struggles with memory, reasoning, and mental clarity, as if a thick fog has settled over their intellect.",properties:[{type:"stat_bonus",target:"INT",value:-4,description:"-4 Intelligence while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},clumsiness:{id:"clumsiness",name:"Cursed: Clumsiness",description:"The cursed item disrupts the wearer's coordination and reflexes. Simple tasks become fumbling ordeals, and the wearer constantly drops things, trips over nothing, and fails at feats of agility they once performed effortlessly.",properties:[{type:"stat_bonus",target:"DEX",value:-4,description:"-4 Dexterity while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},frailty:{id:"frailty",name:"Cursed: Frailty",description:"The item drains vitality from the wearer, leaving them weak and sickly. Wounds heal slowly, diseases take hold more easily, and the wearer feels constantly exhausted as if recovering from a terrible illness.",properties:[{type:"stat_bonus",target:"CON",value:-4,description:"-4 Constitution while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},foolishness:{id:"foolishness",name:"Cursed: Foolishness",description:"The curse clouds judgment and perception, making the wearer dangerously gullible and unaware. They fail to notice obvious threats, misjudge situations completely, and lose touch with their intuitive and perceptive abilities.",properties:[{type:"stat_bonus",target:"WIS",value:-4,description:"-4 Wisdom while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},repulsiveness:{id:"repulsiveness",name:"Cursed: Repulsiveness",description:"The wearer becomes unnaturally off-putting and difficult to be around. People feel instinctively uncomfortable or repulsed in their presence, and attempts at persuasion or leadership almost always fail despite the wearer's best efforts.",properties:[{type:"stat_bonus",target:"CHA",value:-4,description:"-4 Charisma while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},fireVulnerability:{id:"fire_vulnerability",name:"Cursed: Fire Vulnerability",description:"The cursed item makes the wearer supernaturally susceptible to fire. Even small flames cause agonizing burns, and the wearer takes double damage from any fire-based attack as if their very skin were coated in oil.",properties:[{type:"passive_modifier",target:"vulnerability_fire",value:!0,description:"Vulnerability to fire damage (double damage)"}],appliedAt:new Date().toISOString(),source:"curse"},coldVulnerability:{id:"cold_vulnerability",name:"Cursed: Cold Vulnerability",description:"The curse robs the wearer of all natural resistance to cold. Chilly temperatures become painfully freezing, and the wearer takes double damage from cold attacks as if they have no protection against the bitter chill.",properties:[{type:"passive_modifier",target:"vulnerability_cold",value:!0,description:"Vulnerability to cold damage (double damage)"}],appliedAt:new Date().toISOString(),source:"curse"},lifesteal:{id:"lifesteal",name:"Cursed: Bloodthirst",description:"A malevolent curse that causes the weapon to feed on its wielder's vitality. Each time the weapon strikes a foe, it also tears life force from the one who wields it, draining blood and energy in exchange for the power it grants.",properties:[{type:"special_property",target:"life_drain",value:"1d4",description:"Wielder takes 1d4 necrotic damage when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"curse"},attunement:{id:"attunement",name:"Cursed: Attunement Lock",description:"Once donned, this equipment refuses to be removed by any means short of powerful magic. The item fuses to the wearer's body, becoming like a second skin that cannot be stripped away no matter how desperately they wish to be free of it.",properties:[{type:"special_property",target:"curse_attunement",value:!0,description:"Once equipped, cannot be removed unless targeted by remove curse"}],appliedAt:new Date().toISOString(),source:"curse"},berserker:{id:"berserker",name:"Cursed: Berserker Rage",description:"The cursed weapon fills the wielder with uncontrollable battle fury. They must attack every round without pause or be overwhelmed by the curse's wrath, leaving them disadvantaged as the weapon punishes their hesitation.",properties:[{type:"special_property",target:"berserker_rage",value:!0,description:"Must attack each round or take disadvantage on all attacks"},{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls (while berserking)"}],appliedAt:new Date().toISOString(),source:"curse"},heavyBurden:{id:"heavy_burden",name:"Cursed: Heavy Burden",description:"The cursed equipment becomes impossibly heavy, as if made of solid lead. Every movement becomes a struggle, and the wearer moves with painful slowness under the crushing weight that only they can feel.",properties:[{type:"special_property",target:"weight_multiplier",value:2,description:"Equipment weight is doubled"},{type:"passive_modifier",target:"speed",value:-5,description:"-5 walking speed"}],appliedAt:new Date().toISOString(),source:"curse"},lightSensitivity:{id:"light_sensitivity",name:"Cursed: Light Sensitivity",description:"The cursed armor or weapon makes the wearer painfully sensitive to light. Bright daylight or magical illumination causes agonizing pain and impairs vision, leaving the wearer disadvantaged in any well-lit environment.",properties:[{type:"special_property",target:"light_sensitivity",value:!0,description:"Disadvantage on attacks and perception in bright light"}],appliedAt:new Date().toISOString(),source:"curse"},invisibility:{id:"invisibility",name:"Cursed: Invisibility",description:"A twisted version of invisibility that hides the wearer from sight but impairs their ability to interact with the world. The wearer cannot see themselves, and their own movements become uncertain and clumsy while invisible.",addsFeatures:[{id:"curse_invisibility_feature",name:"Cursed Invisibility",description:"You are invisible while equipped. However, you have disadvantage on attack rolls and enemies have advantage on attacks against you before you are revealed.",effects:[{type:"ability_unlock",target:"invisibility",value:!0,description:"Invisible"},{type:"passive_modifier",target:"attack_roll",value:-5,description:"Disadvantage on attacks while invisible"}],source:"equipment_inline"}],properties:[],appliedAt:new Date().toISOString(),source:"curse"},hallucinations:{id:"hallucinations",name:"Cursed: Hallucinations",description:"The cursed item causes terrifying hallucinations that distort reality. Allies appear as enemies, enemies as trusted friends, and the wearer cannot trust their own senses as the line between nightmare and reality blurs.",properties:[{type:"special_property",target:"hallucinations",value:"25_percent",description:"25% chance each round to see enemies as allies and vice versa"}],appliedAt:new Date().toISOString(),source:"curse"},bloodMoney:{id:"blood_money",name:"Cursed: Blood Money",description:"A cruel curse that converts violence into pain for the wielder. Every successful strike causes wounds to mysteriously appear on the wielder's body, as if the weapon siphons blood in exchange for the damage it deals.",properties:[{type:"special_property",target:"hp_on_damage",value:"1d4",description:"Wielder takes 1d4 damage when dealing damage to enemies",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"curse"}},ALL_ENCHANTMENTS:{}};_.ALL_ENCHANTMENTS.plusOne=_.WEAPON_ENCHANTMENTS.plusOne;_.ALL_ENCHANTMENTS.plusTwo=_.WEAPON_ENCHANTMENTS.plusTwo;_.ALL_ENCHANTMENTS.plusThree=_.WEAPON_ENCHANTMENTS.plusThree;_.ALL_ENCHANTMENTS.flaming=_.WEAPON_ENCHANTMENTS.flaming;_.ALL_ENCHANTMENTS.frost=_.WEAPON_ENCHANTMENTS.frost;_.ALL_ENCHANTMENTS.shocking=_.WEAPON_ENCHANTMENTS.shocking;_.ALL_ENCHANTMENTS.thundering=_.WEAPON_ENCHANTMENTS.thundering;_.ALL_ENCHANTMENTS.acidic=_.WEAPON_ENCHANTMENTS.acidic;_.ALL_ENCHANTMENTS.poison=_.WEAPON_ENCHANTMENTS.poison;_.ALL_ENCHANTMENTS.holy=_.WEAPON_ENCHANTMENTS.holy;_.ALL_ENCHANTMENTS.vampiric=_.WEAPON_ENCHANTMENTS.vampiric;_.ALL_ENCHANTMENTS.vorpalEdge=_.WEAPON_ENCHANTMENTS.vorpalEdge;_.ALL_ENCHANTMENTS.keenEdge=_.WEAPON_ENCHANTMENTS.keenEdge;_.ALL_ENCHANTMENTS.mighty=_.WEAPON_ENCHANTMENTS.mighty;_.ALL_ENCHANTMENTS.returning=_.WEAPON_ENCHANTMENTS.returning;_.ALL_ENCHANTMENTS.lifestealing=_.WEAPON_ENCHANTMENTS.lifestealing;_.ALL_ENCHANTMENTS.plusOneArmor=_.ARMOR_ENCHANTMENTS.plusOne;_.ALL_ENCHANTMENTS.plusTwoArmor=_.ARMOR_ENCHANTMENTS.plusTwo;_.ALL_ENCHANTMENTS.fire=_.RESISTANCE_ENCHANTMENTS.fire;_.ALL_ENCHANTMENTS.cold=_.RESISTANCE_ENCHANTMENTS.cold;_.ALL_ENCHANTMENTS.lightning=_.RESISTANCE_ENCHANTMENTS.lightning;_.ALL_ENCHANTMENTS.acid=_.RESISTANCE_ENCHANTMENTS.acid;_.ALL_ENCHANTMENTS.poisonResistance=_.RESISTANCE_ENCHANTMENTS.poison;_.ALL_ENCHANTMENTS.necrotic=_.RESISTANCE_ENCHANTMENTS.necrotic;_.ALL_ENCHANTMENTS.radiant=_.RESISTANCE_ENCHANTMENTS.radiant;_.ALL_ENCHANTMENTS.thunder=_.RESISTANCE_ENCHANTMENTS.thunder;_.ALL_ENCHANTMENTS.allResistance=_.RESISTANCE_ENCHANTMENTS.all;_.ALL_ENCHANTMENTS.holyAvenger=_.COMBO_ENCHANTMENTS.holyAvenger;_.ALL_ENCHANTMENTS.dragonSlayer=_.COMBO_ENCHANTMENTS.dragonSlayer;_.ALL_ENCHANTMENTS.demonHunter=_.COMBO_ENCHANTMENTS.demonHunter;_.ALL_ENCHANTMENTS.undeadBane=_.COMBO_ENCHANTMENTS.undeadBane;class be{constructor(){throw new Error("EnchantmentLibrary is a utility class and cannot be instantiated")}static getEnchantment(e){return Object.values(_.ALL_ENCHANTMENTS).find(t=>t.id===e)}static getCurse(e){return Object.values(_.CURSES).find(t=>t.id===e)}static getAllEnchantments(){return Object.values(_.ALL_ENCHANTMENTS)}static getAllCurses(){return Object.values(_.CURSES)}static getEnchantmentsByType(e){switch(e){case"weapon":return Object.values(_.WEAPON_ENCHANTMENTS);case"armor":return Object.values(_.ARMOR_ENCHANTMENTS);case"resistance":return Object.values(_.RESISTANCE_ENCHANTMENTS);case"combo":return Object.values(_.COMBO_ENCHANTMENTS);default:return[]}}static createStrengthEnchantment(e){return{id:`strength_${e}`,name:`Strength +${e}`,properties:[{type:"stat_bonus",target:"STR",value:e,description:`+${e} Strength`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createDexterityEnchantment(e){return{id:`dexterity_${e}`,name:`Dexterity +${e}`,properties:[{type:"stat_bonus",target:"DEX",value:e,description:`+${e} Dexterity`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createConstitutionEnchantment(e){return{id:`constitution_${e}`,name:`Constitution +${e}`,properties:[{type:"stat_bonus",target:"CON",value:e,description:`+${e} Constitution`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createIntelligenceEnchantment(e){return{id:`intelligence_${e}`,name:`Intelligence +${e}`,properties:[{type:"stat_bonus",target:"INT",value:e,description:`+${e} Intelligence`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createWisdomEnchantment(e){return{id:`wisdom_${e}`,name:`Wisdom +${e}`,properties:[{type:"stat_bonus",target:"WIS",value:e,description:`+${e} Wisdom`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createCharismaEnchantment(e){return{id:`charisma_${e}`,name:`Charisma +${e}`,properties:[{type:"stat_bonus",target:"CHA",value:e,description:`+${e} Charisma`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}}const up=_.WEAPON_ENCHANTMENTS,dp=_.ARMOR_ENCHANTMENTS,hp=_.RESISTANCE_ENCHANTMENTS,pp=_.CURSES,mp=_.ALL_ENCHANTMENTS,gp=s=>be.getEnchantment(s),fp=s=>be.getCurse(s),yp=()=>be.getAllEnchantments(),vp=()=>be.getAllCurses(),bp=s=>be.getEnchantmentsByType(s),wp=s=>be.createStrengthEnchantment(s),Sp=s=>be.createDexterityEnchantment(s),_p=s=>be.createConstitutionEnchantment(s),Ap=s=>be.createIntelligenceEnchantment(s),kp=s=>be.createWisdomEnchantment(s),Tp=s=>be.createCharismaEnchantment(s);function Cp(s){return tt.find(e=>e.name===s)}function Ip(s){return tt.filter(e=>e.type===s)}function Ep(s){return tt.filter(e=>e.rarity===s)}function Mp(){return tt.filter(s=>s.tags?.includes("cursed"))}function Dp(s){return tt.filter(e=>e.properties?.some(t=>t.type===s))}function Rp(s,e){const t=Ft[e];return t?{...s,...t,name:`${s.name} (${e.replace("_"," ")})`,templateId:e,properties:[...s.properties||[],...t.properties||[]],tags:[...s.tags||[],...t.tags||[]]}:null}exports.ALL_GRID_TYPES=y.ALL_GRID_TYPES;exports.BEAT_ACCURACY_THRESHOLDS=y.BEAT_ACCURACY_THRESHOLDS;exports.BEAT_DETECTION_ALGORITHM=y.BEAT_DETECTION_ALGORITHM;exports.BEAT_DETECTION_VERSION=y.BEAT_DETECTION_VERSION;exports.BeatInterpolator=y.BeatInterpolator;exports.BeatMapGenerator=y.BeatMapGenerator;exports.BeatStream=y.BeatStream;exports.BeatTracker=y.BeatTracker;exports.CONSECUTIVE_KEY_LIMITS=y.CONSECUTIVE_KEY_LIMITS;exports.CompositeStreamGenerator=y.CompositeStreamGenerator;exports.DDR_PATTERN_LIBRARY=y.DDR_PATTERN_LIBRARY;exports.DEFAULT_BEATMAP_GENERATOR_OPTIONS=y.DEFAULT_BEATMAP_GENERATOR_OPTIONS;exports.DEFAULT_BEATSTREAM_OPTIONS=y.DEFAULT_BEATSTREAM_OPTIONS;exports.DEFAULT_BEAT_INTERPOLATION_OPTIONS=y.DEFAULT_BEAT_INTERPOLATION_OPTIONS;exports.DEFAULT_BUTTON_MAPPING_CONFIG=y.DEFAULT_BUTTON_MAPPING_CONFIG;exports.DEFAULT_DOWNBEAT_CONFIG=y.DEFAULT_DOWNBEAT_CONFIG;exports.DEFAULT_GROOVE_OPTIONS=y.DEFAULT_GROOVE_OPTIONS;exports.DEFAULT_RHYTHMIC_BALANCE_CONFIG=y.DEFAULT_RHYTHMIC_BALANCE_CONFIG;exports.DEFAULT_SUBDIVISION_CONFIG=y.DEFAULT_SUBDIVISION_CONFIG;exports.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS=y.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS;exports.DEFAULT_TEMPO_AWARE_CONFIG=y.DEFAULT_TEMPO_AWARE_CONFIG;exports.DEFAULT_TIME_SIGNATURE=y.DEFAULT_TIME_SIGNATURE;exports.DensityAnalyzer=y.DensityAnalyzer;exports.DifficultyVariantGenerator=y.DifficultyVariantGenerator;exports.EASY_ACCURACY_THRESHOLDS=y.EASY_ACCURACY_THRESHOLDS;exports.EASY_GROOVE_PENALTIES=y.EASY_GROOVE_PENALTIES;exports.EASY_QUARTER_NOTE_BPM=y.EASY_QUARTER_NOTE_BPM;exports.GAUSSIAN_SMOOTH_PRESETS=y.GAUSSIAN_SMOOTH_PRESETS;exports.GROOVE_PENALTY_PRESETS=y.GROOVE_PENALTY_PRESETS;exports.GROOVE_TIERS=y.GROOVE_TIERS;exports.GUITAR_HERO_PATTERN_LIBRARY=y.GUITAR_HERO_PATTERN_LIBRARY;exports.HARD_ACCURACY_THRESHOLDS=y.HARD_ACCURACY_THRESHOLDS;exports.HARD_GROOVE_PENALTIES=y.HARD_GROOVE_PENALTIES;exports.HARD_RESTRICT_BPM=y.HARD_RESTRICT_BPM;exports.HOP_SIZE_PRESETS=y.HOP_SIZE_PRESETS;exports.HighBpmGridRestrictionRule=y.HighBpmGridRestrictionRule;exports.MAX_BEATS_PER_MEASURE=y.MAX_BEATS_PER_MEASURE;exports.MAX_SUBDIVISION_DENSITY=y.MAX_SUBDIVISION_DENSITY;exports.MEDIUM_ACCURACY_THRESHOLDS=y.MEDIUM_ACCURACY_THRESHOLDS;exports.MEDIUM_GROOVE_PENALTIES=y.MEDIUM_GROOVE_PENALTIES;exports.MEDIUM_RESTRICT_BPM=y.MEDIUM_RESTRICT_BPM;exports.MEL_BANDS_PRESETS=y.MEL_BANDS_PRESETS;exports.MIN_BEATS_PER_MEASURE=y.MIN_BEATS_PER_MEASURE;exports.MultiBandAnalyzer=y.MultiBandAnalyzer;exports.OnsetStrengthEnvelope=y.OnsetStrengthEnvelope;exports.PhraseAnalyzer=y.PhraseAnalyzer;exports.PitchDetector=y.PitchDetector;exports.RHYTHM_PRESETS=y.RHYTHM_PRESETS;exports.RhythmGenerator=y.RhythmGenerator;exports.RhythmQuantizer=y.RhythmQuantizer;exports.RhythmicBalancer=y.RhythmicBalancer;exports.SUBDIVISION_LIMITS=y.SUBDIVISION_LIMITS;exports.SpectrumScanner=y.SpectrumScanner;exports.StreamScorer=y.StreamScorer;exports.TempoAwareQuantizer=y.TempoAwareQuantizer;exports.TempoDetector=y.TempoDetector;exports.TransientDetector=y.TransientDetector;exports.VALID_SUBDIVISION_TYPES=y.VALID_SUBDIVISION_TYPES;exports.calculateBeatInMeasure=y.calculateBeatInMeasure;exports.calculateMaxAchievableDensity=y.calculateMaxAchievableDensity;exports.calculateStdDev=y.calculateStdDev;exports.chartedBeatMapToBeatMap=y.chartedBeatMapToBeatMap;exports.convertToAllowedGridType=y.convertToAllowedGridType;exports.createMelFilterbank=y.createMelFilterbank;exports.deriveAllowedGridTypes=y.deriveAllowedGridTypes;exports.deriveSeed=y.deriveSeed;exports.findActiveSegment=y.findActiveSegment;exports.gaussianSmooth=y.gaussianSmooth;exports.generateSeed=y.generateSeed;exports.getAccuracyThresholdsForPreset=y.getAccuracyThresholdsForPreset;exports.getAllowedGridTypes=y.getAllowedGridTypes;exports.getConsecutiveKeyLimit=y.getConsecutiveKeyLimit;exports.getControllerModeBalanceDefaults=y.getControllerModeBalanceDefaults;exports.getControllerModeScoringDefaults=y.getControllerModeScoringDefaults;exports.getGaussianSmoothMs=y.getGaussianSmoothMs;exports.getGroovePenaltiesForPreset=y.getGroovePenaltiesForPreset;exports.getGrooveTier=y.getGrooveTier;exports.getGrooveWindowMs=y.getGrooveWindowMs;exports.getHopSizeMs=y.getHopSizeMs;exports.getMelBands=y.getMelBands;exports.getMetricGroupSize=y.getMetricGroupSize;exports.getMinHotnessForTier=y.getMinHotnessForTier;exports.getPatternById=y.getPatternById;exports.getPatternLibrary=y.getPatternLibrary;exports.getPatternLibraryStats=y.getPatternLibraryStats;exports.getPatternsByCategory=y.getPatternsByCategory;exports.getPatternsByDifficulty=y.getPatternsByDifficulty;exports.getPatternsByKeyCount=y.getPatternsByKeyCount;exports.getPatternsByTags=y.getPatternsByTags;exports.getRandomPattern=y.getRandomPattern;exports.getRhythmPreset=y.getRhythmPreset;exports.getRhythmPresetNames=y.getRhythmPresetNames;exports.getSubdivisionDensity=y.getSubdivisionDensity;exports.getTempoAwareAllowedGridTypes=y.getTempoAwareAllowedGridTypes;exports.hashSeedToFloat=y.hashSeedToFloat;exports.hashSeedToInt=y.hashSeedToInt;exports.highPassFilter=y.highPassFilter;exports.hzToMel=y.hzToMel;exports.isFullBeatMapExportData=y.isFullBeatMapExportData;exports.isGridTypeAllowed=y.isGridTypeAllowed;exports.isLevelPackExport=y.isLevelPackExport;exports.isMetricStrongBeat=y.isMetricStrongBeat;exports.isMetricWeakBeat=y.isMetricWeakBeat;exports.isStrongBeatForEmphasis=y.isStrongBeatForEmphasis;exports.isValidSubdivisionType=y.isValidSubdivisionType;exports.mapGridToSubdivision=y.mapGridToSubdivision;exports.melToHz=y.melToHz;exports.mergeButtonMappingConfig=y.mergeButtonMappingConfig;exports.naturalDifficultyToLevel=y.naturalDifficultyToLevel;exports.performBeatFFT=y.performFFT;exports.performSTFT=y.performSTFT;exports.reapplyDownbeatConfig=y.reapplyDownbeatConfig;exports.resampleAudio=y.resampleAudio;exports.unifyBeatMap=y.unifyBeatMap;exports.validateButtonMappingConfig=y.validateButtonMappingConfig;exports.validateDownbeatConfig=y.validateDownbeatConfig;exports.validateDownbeatConfigAgainstBeats=y.validateDownbeatConfigAgainstBeats;exports.validateSubdivisionConfig=y.validateSubdivisionConfig;exports.validateSubdivisionConfigAgainstBeats=y.validateSubdivisionConfigAgainstBeats;exports.validateSubdivisionDensity=y.validateSubdivisionDensity;exports.validateSubdivisionLimits=y.validateSubdivisionLimits;exports.validateThresholds=y.validateThresholds;exports.validateTrackMatch=y.validateTrackMatch;exports.ArweaveGatewayManager=z.ArweaveGatewayManager;exports.DEFAULT_GATEWAYS=z.DEFAULT_GATEWAYS;exports.KNOWN_GATEWAY_HOSTS=z.KNOWN_GATEWAY_HOSTS;exports.LogLevel=z.LogLevel;exports.Logger=z.Logger;exports.arweaveGatewayManager=z.arweaveGatewayManager;exports.constructGatewayUrl=z.constructGatewayUrl;exports.createLogger=z.createLogger;exports.getAllGatewayUrls=z.getAllGatewayUrls;exports.isArweaveUrl=z.isArweaveUrl;exports.parseArweaveUrl=z.parseArweaveUrl;exports.DEFAULT_IPFS_GATEWAY=O.DEFAULT_IPFS_GATEWAY;exports.KNOWN_IPFS_GATEWAY_HOSTS=O.KNOWN_IPFS_GATEWAY_HOSTS;exports.MetadataExtractor=O.MetadataExtractor;exports.extractIPFSPath=O.extractIPFSPath;exports.getArtists=O.getArtists;exports.getAudioUrls=O.getAudioUrls;exports.getFullTracks=O.getFullTracks;exports.getGenres=O.getGenres;exports.getImageUrls=O.getImageUrls;exports.getTags=O.getTags;exports.getTotalDuration=O.getTotalDuration;exports.getTrackCount=O.getTrackCount;exports.getTrackTitles=O.getTrackTitles;exports.getTracks=O.getTracks;exports.getVRMTracks=O.getVRMTracks;exports.getVRMs=O.getVRMs;exports.isIPFS=O.isIPFS;exports.resolveIPFSLink=O.resolveIPFSLink;exports.AICombatRunner=_s;exports.ALL_CLASSES=Ds;exports.ALL_ENCHANTMENTS=mp;exports.ALL_RACES=Ps;exports.ARMOR_ENCHANTMENTS=dp;exports.AbilityScoreCalculator=Et;exports.AbilityScoresSchema=ma;exports.AppearanceGenerator=Nn;exports.AttackResolver=ue;exports.AudioProfileSchema=Yd;exports.BASE_PLAYS_THRESHOLD=hs;exports.BASE_XP_THRESHOLD=ps;exports.BalanceValidator=ro;exports.BalancedStrategy=Bn;exports.BeatSubdivider=Qe;exports.BoxOpener=qn;exports.CLASS_AUDIO_PREFERENCES=Rs;exports.CLASS_DATA=Ce;exports.CLASS_SPELL_LISTS=Rt;exports.CLASS_STARTING_EQUIPMENT=ba;exports.CR_TO_XP=Mt;exports.CURSES=pp;exports.CharacterGenerator=Vt;exports.CharacterSheetSchema=Zd;exports.CharacterUpdater=Gh;exports.ClassSuggester=Bt;exports.ColorExtractor=Mh;exports.CombatAI=gs;exports.CombatEngine=ws;exports.CombatMetricsTracker=Ss;exports.CombatSimulator=pt;exports.CombatantAccumulator=Hs;exports.ComparativeAnalyzer=lo;exports.DEFAULT_CLASS_FEATURES=ga;exports.DEFAULT_ENEMY_TEMPLATES=Xe;exports.DEFAULT_EQUIPMENT=Ze;exports.DEFAULT_PROGRESSION_CONFIG=Ue;exports.DEFAULT_RACE_DATA_ARRAY=$s;exports.DEFAULT_RACIAL_TRAITS=fa;exports.DEFAULT_RHYTHM_XP_CONFIG=Me;exports.DEFAULT_SENSOR_CONFIG=Jn;exports.DEFAULT_SKILLS=ya;exports.DEFAULT_SKILL_CATEGORIES=ch;exports.DiceRoller=U;exports.DifficultyCalculator=ca;exports.DnD5eSmartStrategy=On;exports.DnD5eStandardStrategy=Ln;exports.ENCHANTMENT_LIBRARY=_;exports.ENEMY_COUNT_MULTIPLIER=na;exports.EXPECTED_WIN_RATES=Pa;exports.EnchantmentLibrary=be;exports.EnemyGenerator=Ke;exports.EnvironmentalSensors=sp;exports.EquipmentEffectApplier=le;exports.EquipmentGenerator=dt;exports.EquipmentModifier=Vh;exports.EquipmentSpawnHelper=Yh;exports.EquipmentValidator=Dt;exports.ExtensionManager=R;exports.FeatureEffectApplier=ht;exports.FeatureQuery=re;exports.FeatureValidator=Fe;exports.GamingPlatformSensors=rp;exports.GrooveAnalyzer=Rh;exports.ITEM_CREATION_TEMPLATES=Ft;exports.InitiativeRoller=fs;exports.LevelUpProcessor=ke;exports.MAGIC_ITEMS=tt;exports.MASTERY_BONUS_XP=xs;exports.MASTERY_THRESHOLD=zr;exports.MAX_PRESTIGE_LEVEL=st;exports.ManualStrategy=Wn;exports.NamingEngine=Sn;exports.PRESTIGE_ROMAN_NUMERALS=ds;exports.PRESTIGE_SCALING_FACTOR=ia;exports.PROFICIENCY_BONUS=Ma;exports.ParameterSweep=oo;exports.PartyAnalyzer=Sh;exports.PlaylistParser=Eh;exports.PlaylistTrackSchema=vn;exports.PrestigeSystem=se;exports.PrimaryOnlyStrategy=Fn;exports.RACE_DATA=Gr;exports.RARITY_CONFIGS=Ca;exports.RESISTANCE_ENCHANTMENTS=hp;exports.RaceSelector=Pn;exports.RandomStrategy=Hn;exports.RhythmXPCalculator=jh;exports.SKILL_ABILITY_MAP=Wr;exports.SPELL_DATABASE=Ia;exports.SPELL_SLOTS_BY_CLASS=oa;exports.SeededDiceRoller=ks;exports.SeededRNG=Se;exports.SensorDashboard=li;exports.ServerlessPlaylistSchema=Vd;exports.SessionTracker=qh;exports.SimulationAggregator=Fs;exports.SkillAssigner=xn;exports.SkillQuery=pe;exports.SkillValidator=we;exports.SpellCaster=V;exports.SpellManager=va;exports.SpellQuery=Ie;exports.SpellValidator=De;exports.StatManager=zn;exports.SubdivisionPlaybackController=Hh;exports.TUNING_FACTORS=sr;exports.WEAPON_ENCHANTMENTS=up;exports.WeightedSelector=ye;exports.XPCalculator=ti;exports.XP_BUDGET_PER_LEVEL=ys;exports.XP_THRESHOLDS=ct;exports.applyTemplate=Rp;exports.applyTuning=nr;exports.areAppearanceDefaultsInitialized=_n;exports.areClassDefaultsInitialized=En;exports.areEquipmentDefaultsInitialized=kn;exports.areFeatureDefaultsInitialized=Mn;exports.areRaceDataDefaultsInitialized=Cn;exports.areRaceDefaultsInitialized=Tn;exports.areSkillDefaultsInitialized=Dn;exports.areSpellDefaultsInitialized=An;exports.asClass=Cs;exports.assignKeyToBeat=xh;exports.assignKeysToBeats=Nh;exports.calculateAdjustedXP=rr;exports.clearAllKeys=Lh;exports.createCharismaEnchantment=Tp;exports.createConstitutionEnchantment=_p;exports.createDexterityEnchantment=Sp;exports.createIntelligenceEnchantment=Ap;exports.createSeededRoller=As;exports.createStatIncreaseStrategy=_a;exports.createStrengthEnchantment=wp;exports.createWisdomEnchantment=kp;exports.displayEnvironmentalDiagnostics=ri;exports.displayGamingDiagnostics=oi;exports.displaySystemDashboard=Kn;exports.ensureAllDefaultsInitialized=gh;exports.ensureAppearanceDefaultsInitialized=za;exports.ensureClassDefaultsInitialized=Xa;exports.ensureEquipmentDefaultsInitialized=Ga;exports.ensureFeatureDefaultsInitialized=Ja;exports.ensureRaceDataDefaultsInitialized=In;exports.ensureRaceDefaultsInitialized=Va;exports.ensureSkillDefaultsInitialized=Rn;exports.ensureSpellDefaultsInitialized=Ot;exports.evaluateMixConditions=nh;exports.extractKeyMap=$h;exports.getAllCurses=vp;exports.getAllEnchantments=yp;exports.getAllRarities=Ts;exports.getAveragePartyLevel=bs;exports.getCRFromXP=Ta;exports.getClassData=Ea;exports.getClassSpellList=la;exports.getClassStartingEquipment=$n;exports.getCurse=fp;exports.getCursedItems=Mp;exports.getEnchantment=gp;exports.getEnchantmentsByType=bp;exports.getEncounterMultiplier=ot;exports.getFeatureQuery=Vr;exports.getHigherRarity=br;exports.getItemsWithProperty=Dp;exports.getKeyCount=Bh;exports.getMagicItem=Cp;exports.getMagicItemsByRarity=Ep;exports.getMagicItemsByType=Ip;exports.getRaceData=zt;exports.getRaceDataAsync=jr;exports.getRarityConfig=Oe;exports.getSignatureDie=vr;exports.getSkillQuery=Fr;exports.getSpellQuery=qr;exports.getSpellSlotsForClass=Da;exports.getTemplateById=gr;exports.getTemplatesByArchetype=yr;exports.getTemplatesByCategory=fr;exports.getTrackExtras=bn;exports.getTrackMetadata=Xd;exports.getUsedKeys=Fh;exports.getXPBudgetForParty=Wt;exports.getXPBudgetPerLevel=vs;exports.getXPForCR=ka;exports.hasRequiredKeys=Oh;exports.initializeAllDefaults=mh;exports.initializeAppearanceDefaults=Wa;exports.initializeClassDefaults=Za;exports.initializeEquipmentDefaults=Ua;exports.initializeFeatureDefaults=Ka;exports.initializeRaceDataDefaults=Ya;exports.initializeRaceDefaults=ja;exports.initializeSkillDefaults=Qa;exports.initializeSpellDefaults=qa;exports.isPrestigeLevel=tr;exports.isValidAIPlayStyle=ar;exports.isValidClass=ra;exports.isValidEncounterDifficulty=wh;exports.isValidEncounterDifficultyUtil=or;exports.isValidEnemyArchetype=bh;exports.isValidEnemyCategory=yh;exports.isValidEnemyRarity=vh;exports.loadConfigFromEnv=Qn;exports.mergeConfig=op;exports.mergeProgressionConfig=cp;exports.mergeRhythmXPConfig=Pt;exports.resolveSelectedMix=wn;exports.shouldAccuracyBreakCombo=po;exports.subdivideBeatMap=Ph;exports.toPrestigeLevel=ms;exports.validateClassFeature=kr;exports.validateClassFeatures=Cr;exports.validateRacialTrait=Tr;exports.validateRacialTraits=Ir;exports.validateSkill=Mr;exports.validateSkillListDefinition=xr;exports.validateSkillPrerequisites=Nr;exports.validateSkillProficiencies=Pr;exports.validateSkillProficiency=Rr;exports.validateSkills=Dr;exports.validateSpell=$r;exports.validateSpellPrerequisites=Br;exports.validateSpellPrerequisitesSchema=Or;exports.validateSpells=Lr;
|
|
83
|
+
Environmental: ${r?k(`✓ ${n}/${n} Healthy`,S.green,a):k(`⚠ ${n}/${s.environmental.sensors.length} Healthy`,S.yellow,a)}`)}if(s.gaming){const n=s.gaming.steam.isAuthenticated;console.log(` Gaming: ${n?k("✓ Connected",S.green,a):k("✗ Disconnected",S.dim,a)}`)}console.log(""),s.environmental&&ri(s.environmental,t),s.gaming&&oi(s.gaming,t)}const li={displayEnvironmentalDiagnostics:ri,displayGamingDiagnostics:oi,displaySystemDashboard:Kn};class sp{constructor(e,t){this.permissions=new Map,this.logger=z.Logger.for("EnvironmentalSensors"),this.context={timestamp:Date.now()},this.sensorStatuses=new Map,this.failureLog=[],this.lastKnownGood=new Map,this.recoveryCallbacks=new Set,this.retryConfig={enabled:!0,maxRetries:3,initialDelayMs:1e3,maxDelayMs:1e4,backoffMultiplier:2},this.xpConfig={maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},this.permissions.set("geolocation",!1),this.permissions.set("motion",!1),this.permissions.set("weather",!1);let a="",i;if(typeof e=="string"||e===void 0)a=e??"",t&&(this.retryConfig={...this.retryConfig,...t});else{const n=e;a=n.weather?.apiKey??"",i=n.geolocation,n.retry&&(this.retryConfig={...this.retryConfig,...n.retry}),n.xpModifier&&(this.xpConfig={...this.xpConfig,...n.xpModifier})}this.geolocation=i?new ls(i):new ls,this.motion=new Zh,this.weather=new ep(a),this.initializeSensorStatuses()}initializeSensorStatuses(){const e=["geolocation","motion","weather"];for(const t of e)this.sensorStatuses.set(t,{type:t,health:"unknown",lastSuccessTimestamp:null,lastFailureTimestamp:null,consecutiveFailures:0,totalFailures:0,lastError:null,isRetrying:!1})}updateSensorStatus(e,t,a){const i=this.sensorStatuses.get(e);if(!i)return;const n=Date.now(),r=i.health;if(t)i.lastSuccessTimestamp=n,i.consecutiveFailures=0,i.lastError=null,i.isRetrying=!1,i.health==="failed"||i.health==="unknown"?(i.health="healthy",this.notifyRecovery(e,r,"healthy","Sensor recovered successfully")):i.health==="degraded"&&(i.health="healthy",this.notifyRecovery(e,r,"healthy","Sensor returned to healthy state"));else if(i.lastFailureTimestamp=n,i.consecutiveFailures++,i.totalFailures++,i.lastError=a||"Unknown error",i.consecutiveFailures>=3){if(i.health!=="failed"){const o=i.health;i.health="failed",this.notifyRecovery(e,o,"failed",`Sensor failed after ${i.consecutiveFailures} consecutive failures`)}}else i.consecutiveFailures>=1&&(i.health==="healthy"||i.health==="unknown")&&(i.health="degraded",this.notifyRecovery(e,r,"degraded",`Sensor degraded after ${i.consecutiveFailures} consecutive failures`));this.sensorStatuses.set(e,i)}notifyRecovery(e,t,a,i){const n={sensorType:e,previousStatus:t,newStatus:a,timestamp:Date.now(),message:i};this.recoveryCallbacks.forEach(r=>{try{r(n)}catch(o){this.logger.error("Recovery callback error",{error:o})}})}logFailure(e,t,a,i){const n={sensorType:e,timestamp:Date.now(),error:t,retryAttempt:a,willRetry:i};this.failureLog.push(n),this.failureLog.length>100&&(this.failureLog=this.failureLog.slice(-100))}storeLastKnownGood(e,t){this.lastKnownGood.set(e,{...this.lastKnownGood.get(e),...t})}async retrySensorOperation(e,t,a="sensor operation"){let i=null,n=this.retryConfig.initialDelayMs;for(let o=0;o<=this.retryConfig.maxRetries;o++)try{if(o>0){const c=this.sensorStatuses.get(e);c&&(c.isRetrying=!0,this.sensorStatuses.set(e,c))}const l=await t();return this.updateSensorStatus(e,!0),l}catch(l){i=l;const c=i.message||String(l),u=o<this.retryConfig.maxRetries;this.logFailure(e,c,o,u),this.updateSensorStatus(e,!1,c),u&&(await this.delay(n),n=Math.min(n*this.retryConfig.backoffMultiplier,this.retryConfig.maxDelayMs))}const r=this.sensorStatuses.get(e);return r&&(r.isRetrying=!1,this.sensorStatuses.set(e,r)),null}delay(e){return new Promise(t=>setTimeout(t,e))}onSensorRecovery(e){return this.recoveryCallbacks.add(e),()=>this.recoveryCallbacks.delete(e)}getSensorStatus(e){return this.sensorStatuses.get(e)||null}getAllSensorStatuses(){return Array.from(this.sensorStatuses.values())}getFailureLog(e,t){let a=[...this.failureLog];return e&&(a=a.filter(i=>i.sensorType===e)),a.reverse(),t&&(a=a.slice(0,t)),a}getLastKnownGood(e){return this.lastKnownGood.get(e)||null}clearFailureLog(){this.failureLog=[]}updateRetryConfig(e){this.retryConfig={...this.retryConfig,...e}}async requestPermissions(e){const t=[];for(const a of e){let i=!1;switch(a){case"geolocation":i=await this.requestGeolocationPermission();break;case"motion":i=await this.requestMotionPermission();break;case"weather":i=!0;break}this.permissions.set(a,i),t.push({type:a,granted:i,timestamp:Date.now()})}return t}startMonitoring(e){this.permissions.get("motion")&&this.motion.startMonitoring(t=>{this.context.motion=t,this.context.timestamp=Date.now(),this.storeLastKnownGood("motion",{motion:t}),this.updateSensorStatus("motion",!0),e&&e(this.context)}),this.updateSnapshot()}stopMonitoring(){this.motion.stopMonitoring()}async updateSnapshot(){if(this.permissions.get("geolocation")){const e=await this.retrySensorOperation("geolocation",()=>this.geolocation.getCurrentPosition(),"getCurrentPosition");if(e){this.context.geolocation=e,this.storeLastKnownGood("geolocation",{geolocation:e});const t=this.geolocation.getBiome(e.latitude,e.longitude,e.altitude);this.context.biome=t}else{const t=this.getLastKnownGood("geolocation");t?.geolocation&&(this.context.geolocation=t.geolocation)}if((this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation)&&this.permissions.get("weather")){const t=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;if(t){const a=await this.retrySensorOperation("weather",()=>this.weather.getWeather(t.latitude,t.longitude),"getWeather");if(a)this.context.weather=a,this.storeLastKnownGood("weather",{weather:a});else{const i=this.getLastKnownGood("weather");i?.weather&&(this.context.weather=i.weather)}}}}return this.context.timestamp=Date.now(),this.context}calculateXPModifier(){let e=1;const t=this.context.motion||this.getLastKnownGood("motion")?.motion;if(t){const n=this.motion.detectActivity(t);n==="running"?e+=this.xpConfig.runningBonus:n==="walking"&&(e+=this.xpConfig.walkingBonus)}const a=this.context.weather||this.getLastKnownGood("weather")?.weather;if(a){const n=a.weatherType.toLowerCase();(n.includes("rain")||n.includes("storm"))&&(e+=this.xpConfig.stormBonus),n.includes("snow")&&(e+=this.xpConfig.snowBonus),a.isNight&&(e+=this.xpConfig.nightBonus)}const i=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;return i&&i.altitude&&i.altitude>this.xpConfig.altitudeThreshold&&(e+=this.xpConfig.altitudeBonus),Math.min(e,this.xpConfig.maxModifier)}getXpModifierBreakdown(){const e=[],t=this.context.motion||this.getLastKnownGood("motion")?.motion,a=t?this.motion.detectActivity(t):null;e.push({id:"running",label:"Running",icon:"🏃",bonus:this.xpConfig.runningBonus,active:a==="running"}),e.push({id:"walking",label:"Walking",icon:"🚶",bonus:this.xpConfig.walkingBonus,active:a==="walking"});const i=this.context.weather||this.getLastKnownGood("weather")?.weather,n=i?.weatherType?.toLowerCase()||"";e.push({id:"storm",label:"Stormy Weather",icon:"🌧️",bonus:this.xpConfig.stormBonus,active:n.includes("rain")||n.includes("storm")}),e.push({id:"snow",label:"Snowy Weather",icon:"❄️",bonus:this.xpConfig.snowBonus,active:n.includes("snow")}),e.push({id:"night",label:"Night Time",icon:"🌙",bonus:this.xpConfig.nightBonus,active:i?.isNight??!1});const r=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation,o=r?.altitude&&r.altitude>this.xpConfig.altitudeThreshold;e.push({id:"altitude",label:"High Altitude",icon:"⛰️",bonus:this.xpConfig.altitudeBonus,active:!!o});const l=e.filter(u=>u.active);return{total:Math.min(1+l.reduce((u,d)=>u+d.bonus,0),this.xpConfig.maxModifier),baseValue:1,sources:e,activeBonuses:l}}async calculateXPModifierWithForecast(e=12){let t=this.calculateXPModifier();const a=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;if(a&&this.permissions.get("weather")){const i=await this.retrySensorOperation("weather",()=>this.weather.getUpcomingWeather(a.latitude,a.longitude,e),"getUpcomingWeather");if(i){const n=i.worstWeatherType.toLowerCase();n.includes("thunderstorm")||n.includes("tornado")?t+=.15:(n.includes("snow")&&i.snowProbability>.5||n.includes("rain")&&i.rainProbability>.7)&&(t+=.1),n==="clear"&&!i.willRain&&!i.willSnow&&(t+=.05)}}return Math.min(t,this.xpConfig.maxModifier)}async calculateXPModifierWithSevereWeather(){let e=this.calculateXPModifier(),t=null;const a=this.context.weather||this.getLastKnownGood("weather")?.weather;a&&(t=this.weather.detectSevereWeather(a),t&&(e+=t.xpBonus));const i=Math.min(e,this.xpConfig.maxModifier),n=t?this.weather.getSafetyWarning(t):null;return{modifier:i,severeWeatherAlert:t,safetyWarning:n}}detectSevereWeather(){const e=this.context.weather||this.getLastKnownGood("weather")?.weather;return e?this.weather.detectSevereWeather(e):null}getSevereWeatherWarning(){const e=this.detectSevereWeather();return e?this.weather.getSafetyWarning(e):null}getPermissions(){return Array.from(this.permissions.entries()).map(([e,t])=>({type:e,granted:t,timestamp:Date.now()}))}checkAvailability(e){if(typeof window>"u")return!1;switch(e){case"geolocation":return"geolocation"in navigator;case"motion":return"DeviceMotionEvent"in window;case"weather":return!0;default:return!1}}async requestGeolocationPermission(){if(!this.checkAvailability("geolocation"))return!1;try{return await this.geolocation.getCurrentPosition(),!0}catch{return!1}}async requestMotionPermission(){if(!this.checkAvailability("motion"))return!1;if(typeof DeviceMotionEvent.requestPermission=="function")try{return await DeviceMotionEvent.requestPermission()==="granted"}catch{return!1}return!0}getCurrentActivity(){if(!this.permissions.get("motion"))return"unknown";const e=this.context.motion||this.getLastKnownGood("motion")?.motion;return e?this.motion.detectActivity(e):"unknown"}getDiagnostics(){const e=z.Logger.isDiagnosticMode();return{timestamp:Date.now(),diagnosticMode:e,sensors:Array.from(this.sensorStatuses.values()).map(t=>({type:t.type,status:t,permission:this.permissions.get(t.type)||!1,availability:this.checkAvailability(t.type),lastKnownGood:this.getLastKnownGood(t.type)})),cache:{geolocation:{age:this.geolocation.getCacheAge(),isExpired:this.geolocation.isCacheExpired(),stats:this.geolocation.getCacheStats()},weather:{size:this.weather.getCacheSize(),stats:this.weather.getCacheStats(),ttl:720}},performance:{weatherApi:this.weather.getWeatherApiStatistics(),forecastApi:this.weather.getForecastApiStatistics()},recentFailures:this.getFailureLog(void 0,10),permissions:this.getPermissions(),context:{hasGeolocation:!!this.context.geolocation,hasMotion:!!this.context.motion,hasWeather:!!this.context.weather,hasBiome:!!this.context.biome,timestamp:this.context.timestamp}}}enableDiagnosticMode(){z.Logger.enableDiagnosticMode(),this.logger.info("Diagnostic mode enabled")}disableDiagnosticMode(){z.Logger.disableDiagnosticMode(),this.logger.info("Diagnostic mode disabled")}printDashboard(e){li.displayEnvironmentalDiagnostics(this.getDiagnostics(),e)}getSolarInfo(e){const t=this.context.geolocation||this.getLastKnownGood("geolocation")?.geolocation;return t?this.weather.getSolarInfo(t.latitude,t.longitude,e):null}getSolarInfoForCoords(e,t,a){return this.weather.getSolarInfo(e,t,a)}}class np{constructor(e=""){this.baseUrl="https://api.steampowered.com",this.logger=z.Logger.for("SteamAPIClient"),this.currentGameApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.metadataApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentCurrentGameTimes=[],this.recentMetadataTimes=[],this.maxRecentSamples=100,this.apiKey=e}calculatePercentile(e,t){if(e.length===0)return 0;const a=[...e].sort((n,r)=>n-r),i=Math.ceil(t/100*a.length)-1;return a[Math.max(0,i)]}recordCurrentGameApiCall(e,t){const a=Date.now();t?(this.currentGameApiMetrics.successCount++,this.currentGameApiMetrics.totalTime+=e,this.currentGameApiMetrics.minTime=Math.min(this.currentGameApiMetrics.minTime,e),this.currentGameApiMetrics.maxTime=Math.max(this.currentGameApiMetrics.maxTime,e),this.recentCurrentGameTimes.push(e),this.recentCurrentGameTimes.length>this.maxRecentSamples&&this.recentCurrentGameTimes.shift()):this.currentGameApiMetrics.errorCount++,this.currentGameApiMetrics.lastCallTimestamp=a}recordMetadataApiCall(e,t){const a=Date.now();t?(this.metadataApiMetrics.successCount++,this.metadataApiMetrics.totalTime+=e,this.metadataApiMetrics.minTime=Math.min(this.metadataApiMetrics.minTime,e),this.metadataApiMetrics.maxTime=Math.max(this.metadataApiMetrics.maxTime,e),this.recentMetadataTimes.push(e),this.recentMetadataTimes.length>this.maxRecentSamples&&this.recentMetadataTimes.shift()):this.metadataApiMetrics.errorCount++,this.metadataApiMetrics.lastCallTimestamp=a}getCurrentGameApiMetrics(){return{...this.currentGameApiMetrics}}getCurrentGameApiStatistics(){const e=this.currentGameApiMetrics.successCount+this.currentGameApiMetrics.errorCount,t=this.currentGameApiMetrics.successCount>0?this.currentGameApiMetrics.totalTime/this.currentGameApiMetrics.successCount:0,a=e>0?this.currentGameApiMetrics.successCount/e*100:0;return{average:Math.round(t),min:this.currentGameApiMetrics.minTime===1/0?0:Math.round(this.currentGameApiMetrics.minTime),max:Math.round(this.currentGameApiMetrics.maxTime),totalCalls:e,successRate:Math.round(a*10)/10,p95:Math.round(this.calculatePercentile(this.recentCurrentGameTimes,95)),p99:Math.round(this.calculatePercentile(this.recentCurrentGameTimes,99))}}getMetadataApiMetrics(){return{...this.metadataApiMetrics}}getMetadataApiStatistics(){const e=this.metadataApiMetrics.successCount+this.metadataApiMetrics.errorCount,t=this.metadataApiMetrics.successCount>0?this.metadataApiMetrics.totalTime/this.metadataApiMetrics.successCount:0,a=e>0?this.metadataApiMetrics.successCount/e*100:0;return{average:Math.round(t),min:this.metadataApiMetrics.minTime===1/0?0:Math.round(this.metadataApiMetrics.minTime),max:Math.round(this.metadataApiMetrics.maxTime),totalCalls:e,successRate:Math.round(a*10)/10,p95:Math.round(this.calculatePercentile(this.recentMetadataTimes,95)),p99:Math.round(this.calculatePercentile(this.recentMetadataTimes,99))}}resetPerformanceMetrics(){this.currentGameApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.metadataApiMetrics={successCount:0,errorCount:0,totalTime:0,minTime:1/0,maxTime:0,lastCallTimestamp:null},this.recentCurrentGameTimes=[],this.recentMetadataTimes=[]}async getCurrentGame(e){if(!this.apiKey)return this.logger.warn("Steam API key not provided"),null;const t=performance.now();let a=!1;try{const i=`${this.baseUrl}/ISteamUser/GetPlayerSummaries/v2/?key=${this.apiKey}&steamids=${e}`,n=await fetch(i);if(!n.ok)throw new Error(`Steam API error: ${n.statusText}`);const o=(await n.json()).response?.players?.[0];return o?.gameextrainfo?(a=!0,{name:o.gameextrainfo,appId:parseInt(o.gameid,10),source:"steam"}):null}catch(i){return this.logger.error("Failed to fetch current Steam game",{error:i}),null}finally{const i=performance.now()-t;this.recordCurrentGameApiCall(i,a)}}async getGameMetadata(e){const t=performance.now();let a=!1;try{const i=await this.findAppId(e);if(!i)return{name:e};const n=`https://steamcommunity.com/api/appdetails?appids=${i}`,r=await fetch(n);if(!r.ok)return{name:e,appId:i};const l=(await r.json())[i]?.data;return l?(a=!0,{appId:i,name:l.name||e,genre:l.genres?.map(c=>c.description)||[],description:l.short_description}):{name:e,appId:i}}catch(i){return this.logger.error(`Failed to fetch Steam metadata for ${e}`,{error:i}),{name:e}}finally{const i=performance.now()-t;this.recordMetadataApiCall(i,a)}}async findAppId(e){if(!this.apiKey)return null;try{const t=`${this.baseUrl}/ISteamApps/GetAppList/v2/`,a=await fetch(t);if(!a.ok)return null;const n=(await a.json()).applist?.apps||[],r=e.toLowerCase();return n.find(l=>l.name.toLowerCase()===r)?.appid||null}catch(t){return this.logger.error("Failed to find Steam app ID",{error:t}),null}}async getGameSchema(e){if(!this.apiKey)return null;try{const t=`${this.baseUrl}/ISteamUserStats/GetSchemaForGame/v2/?appid=${e}&key=${this.apiKey}`,a=await fetch(t);return a.ok&&(await a.json()).game||null}catch(t){return this.logger.error("Failed to fetch game schema",{error:t}),null}}}class rp{constructor(e={}){this.pollIntervalMs=6e4,this.pollingInterval=null,this.exponentialBackoff=1,this.maxBackoffMs=6e5,this.logger=z.Logger.for("GamingPlatformSensors"),this.xpConfig={maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},this.gamingContext={isActivelyGaming:!1,platformSource:"none",totalGamingMinutes:0,gamesPlayedWhileListening:[],lastUpdated:Date.now()},this.contextCallback=null,this.gameMetadataCache=new Map,this.cacheExpiryMs=864e5,"steam"in e&&e.steam&&(this.steamApiKey=e.steam.apiKey,this.steamUserId=e.steam.steamId,e.steam.pollInterval&&(this.pollIntervalMs=e.steam.pollInterval)),"metadataCacheExpiry"in e&&e.metadataCacheExpiry&&(this.cacheExpiryMs=e.metadataCacheExpiry),"maxBackoffMs"in e&&e.maxBackoffMs&&(this.maxBackoffMs=e.maxBackoffMs),"xpModifier"in e&&e.xpModifier&&(this.xpConfig={...this.xpConfig,...e.xpModifier}),this.steam=new np(this.steamApiKey)}async authenticate(e){return e?(this.steamUserId=e,!0):!!this.steamUserId}startMonitoring(e){this.pollingInterval||(this.contextCallback=e||null,this.updateGamingStatus(),this.pollingInterval=setInterval(()=>{this.updateGamingStatus()},this.pollIntervalMs))}stopMonitoring(){this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=null),this.contextCallback=null}async updateGamingStatus(){try{const e=this.steamUserId?await this.steam.getCurrentGame(this.steamUserId):null;this.gamingContext={isActivelyGaming:!!e,platformSource:e?"steam":"none",currentGame:e?{name:e.name,source:e.source,genre:await this.getGameMetadata(e.name).then(t=>t?.genre),sessionDuration:e.sessionDuration,partySize:"partySize"in e&&typeof e.partySize=="number"?e.partySize:void 0}:void 0,totalGamingMinutes:this.gamingContext.totalGamingMinutes,gamesPlayedWhileListening:this.gamingContext.gamesPlayedWhileListening,lastUpdated:Date.now()},this.exponentialBackoff=1,this.contextCallback&&this.contextCallback(this.gamingContext)}catch(e){this.logger.warn("Error updating gaming status",{error:e}),this.applyExponentialBackoff()}}applyExponentialBackoff(){this.exponentialBackoff=Math.min(this.exponentialBackoff*2,this.maxBackoffMs/this.pollIntervalMs),this.pollingInterval&&(clearInterval(this.pollingInterval),this.pollingInterval=setInterval(()=>this.updateGamingStatus(),this.pollIntervalMs*this.exponentialBackoff))}async getGameMetadata(e){const t=this.gameMetadataCache.get(e);if(t&&Date.now()-t.lastUpdated<this.cacheExpiryMs)return t;try{const a=await this.steam.getGameMetadata(e);return this.gameMetadataCache.set(e,{genre:a?.genre,lastUpdated:Date.now()}),a}catch(a){return this.logger.warn(`Failed to fetch metadata for game: ${e}`,{error:a}),null}}isPlayingGame(e){const t=this.getContext();return t.currentGame?t.currentGame.name.toLowerCase()===e.toLowerCase():!1}calculateGamingBonus(){const e=this.getContext();if(!e.isActivelyGaming)return 1;let t=this.xpConfig.gamingBaseBonus;const a=e.currentGame;if(a){if(a.genre)for(const i of a.genre){const n=i.toLowerCase();n.includes("rpg")?t+=this.xpConfig.gamingRPGBonus:n.includes("action")?t+=.15:n.includes("strategy")&&(t+=.1)}if(a.partySize&&a.partySize>1&&(t+=this.xpConfig.gamingMultiplayerBonus),a.sessionDuration){const i=a.sessionDuration/60,n=Math.min(i/4*.2,.2);t+=n}}return Math.min(1+t,this.xpConfig.maxGamingModifier)}getContext(){return{...this.gamingContext}}recordGameSession(e,t){this.gamingContext.gamesPlayedWhileListening.includes(e)||this.gamingContext.gamesPlayedWhileListening.push(e),this.gamingContext.totalGamingMinutes+=t}getDiagnostics(){return{timestamp:Date.now(),steam:{isAuthenticated:!!this.steamUserId,userId:this.steamUserId,apiKey:!!this.steamApiKey},gamingContext:this.getContext(),polling:{isActive:this.pollingInterval!==null,intervalMs:this.pollIntervalMs,exponentialBackoff:this.exponentialBackoff},cache:{gameMetadataCacheSize:this.gameMetadataCache.size,cachedGames:Array.from(this.gameMetadataCache.keys())},performance:{currentGameApi:this.steam.getCurrentGameApiStatistics(),metadataApi:this.steam.getMetadataApiStatistics()}}}async fetchGameSchema(e){return this.steam.getGameSchema(e)}printDashboard(e){li.displayGamingDiagnostics(this.getDiagnostics(),e)}}const Jn={geolocation:{cacheTTL:300*1e3,useLocalStorage:!0,enableHighAccuracy:!0,timeout:5e3},weather:{apiKey:"",cacheTTL:720*1e3,forecastCacheTTL:3600*1e3,useLocalStorage:!0},gaming:{steam:{apiKey:"",steamId:void 0,pollInterval:6e4},metadataCacheExpiry:1440*60*1e3,maxBackoffMs:600*1e3},xpModifier:{maxModifier:3,maxGamingModifier:1.75,runningBonus:.5,walkingBonus:.2,stormBonus:.4,snowBonus:.3,nightBonus:.25,altitudeThreshold:1e3,altitudeBonus:.3,gamingBaseBonus:.25,gamingRPGBonus:.2,gamingMultiplayerBonus:.15},retry:{enabled:!0,maxRetries:3,initialDelayMs:1e3,maxDelayMs:1e4,backoffMultiplier:2}};function Qn(){const s={};if(typeof process<"u"&&process.env?.WEATHER_API_KEY&&(s.weather={...s.weather,apiKey:process.env.WEATHER_API_KEY}),typeof process<"u"&&process.env?.STEAM_API_KEY&&(s.gaming={...s.gaming,steam:{...s.gaming?.steam,apiKey:process.env.STEAM_API_KEY}}),typeof process<"u"&&process.env?.STEAM_USER_ID&&(s.gaming={...s.gaming,steam:{...s.gaming?.steam,steamId:process.env.STEAM_USER_ID}}),typeof process<"u"&&process.env?.XP_MAX_MODIFIER){const e=parseFloat(process.env.XP_MAX_MODIFIER);!isNaN(e)&&e>0&&(s.xpModifier={...s.xpModifier,maxModifier:e})}return s}function op(s={}){const e=Qn();return Aa(Jn,Aa(e,s))}function Aa(s,e){const t={...s};for(const a in e){const i=e[a],n=t[a];i!==void 0&&(typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof n=="object"&&n!==null&&!Array.isArray(n)?t[a]=Aa(n,i):t[a]=i)}return t}const lp=[0,300,900,2700,6500,14e3,23e3,34e3,48e3,64e3,85e3,1e5,12e4,14e4,165e3,195e3,225e3,265e3,305e3,355e3],Ue={xp:{level_thresholds:lp,xp_per_second:1,xp_per_track_completion:50,activity_bonuses:{stationary:1,walking:1.2,running:1.5,driving:1.3,night_time:1.25,extreme_weather:1.4,high_altitude:1.3,rhythm_game_base:1.25,rhythm_game_combo:.5,rhythm_game_groove:.5},track_mastery_threshold:10,mastery_bonus_xp:100,rhythmXP:Me},statIncrease:{maxStatCap:20,strategy:"dnD5e",autoApply:!0,statIncreaseLevels:[4,8,12,16,19]},levelUp:{useAverageHP:!1,allowManualStatSelection:!0,showNotifications:!0}};function cp(s){return s?{xp:{...Ue.xp,...s.xp,activity_bonuses:{...Ue.xp.activity_bonuses,...s.xp?.activity_bonuses},rhythmXP:Pt(s.xp?.rhythmXP)},statIncrease:{...Ue.statIncrease,...s.statIncrease},levelUp:{...Ue.levelUp,...s.levelUp}}:Ue}const tt=[{name:"Flame Tongue",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse"],description:"A magic sword that can burst into flame, dealing extra fire damage and shedding light. The wielder can ignite or extinguish the flames as a bonus action.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light in a 20ft radius and dim light for 20ft beyond"}],grantsFeatures:[{id:"flame_tongue_ignition",name:"Ignition",description:"As a bonus action, you can speak the command word to cause the sword to burst into flame. The flames last until you speak the command word again or until you drop or sheathe the sword.",effects:[{type:"special_property",target:"fire_illumination",value:!0,description:"Sword bursts into flame"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","fire","weapon","versatile"]},{name:"Vorpal Sword",type:"weapon",rarity:"legendary",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A legendary weapon that decapitates creatures on a natural 20. One of the most powerful weapons in existence, capable of instantly slaying even the mightiest foes.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls"},{type:"special_property",target:"decapitation",value:"natural_20",description:"On a roll of 20, decapitate creatures with heads",condition:{type:"on_hit",value:!0}}],spawnWeight:0,source:"custom",tags:["magic","weapon","legendary","versatile","artifact"]},{name:"Frost Brand",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"cold",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A freezing cold sword that deals extra cold damage and grants its wielder resistance to fire. Can extinguish non-magical flames with a mere thought.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage on hit"},{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"},{type:"special_property",target:"extinguish_flames",value:!0,description:"Extinguish all non-magical flames within 30ft"}],spawnWeight:.1,source:"custom",tags:["magic","cold","weapon","fire_resistance"]},{name:"Dragonslayer Longsword",type:"weapon",rarity:"very_rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A magical longsword enchanted against dragonkind. Deals devastating extra damage to dragons and glows in the presence of these ancient creatures.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"dragon_damage",value:"2d6",description:"+2d6 damage against dragons",condition:{type:"vs_creature_type",value:"dragon"}}],spawnWeight:.05,source:"custom",tags:["magic","weapon","versatile","dragon_slaying"]},{name:"Lightning Lance",type:"weapon",rarity:"rare",weight:6,damage:{dice:"1d6",damageType:"piercing"},weaponProperties:["reach","two-handed"],description:"A spear crackling with electrical energy that extends your reach. Deals extra lightning damage on hits and can arc to nearby enemies.",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage on hit"},{type:"special_property",target:"lightning_arc",value:"half_damage",description:"On critical hit, lightning arcs to another creature within 10ft for half damage"}],spawnWeight:.1,source:"custom",tags:["magic","weapon","lightning","reach"]},{name:"Mithral Shirt",type:"armor",rarity:"uncommon",weight:10,acBonus:12,description:"Finely crafted armor made of mithral, a light and incredibly strong metal. Counts as light armor for all class features and imposes no disadvantage on Stealth.",properties:[{type:"passive_modifier",target:"ac",value:12,description:"Base AC: 12 + DEX (max 2)"},{type:"special_property",target:"armor_classification",value:"light",description:"Counts as light armor for class features"},{type:"special_property",target:"stealth_normal",value:!0,description:"No stealth disadvantage"}],spawnWeight:.2,source:"custom",tags:["magic","armor","light","mithral"]},{name:"+1 Chain Shirt",type:"armor",rarity:"rare",weight:20,acBonus:14,description:"Magically reinforced chain armor that provides improved protection without adding extra weight. The enchanted metal links deflect blows more effectively than ordinary chain.",properties:[{type:"passive_modifier",target:"ac",value:14,description:"Fixed AC: 14 (13 base + 1 enhancement)"}],spawnWeight:.15,source:"custom",tags:["magic","armor","light","enhanced"]},{name:"+2 Chain Shirt",type:"armor",rarity:"very_rare",weight:20,acBonus:15,description:"Superiorly enchanted chain armor with powerful protective magic. The links are magically hardened to provide exceptional defense while remaining flexible.",properties:[{type:"passive_modifier",target:"ac",value:15,description:"Fixed AC: 15 (13 base + 2 enhancement)"}],spawnWeight:.1,source:"custom",tags:["magic","armor","light","enhanced"]},{name:"+1 Plate Armor",type:"armor",rarity:"rare",weight:65,acBonus:19,description:"Magically enhanced plate armor that provides superior protection. The enchantment improves the armor's protective qualities while maintaining its strength requirement.",properties:[{type:"passive_modifier",target:"ac",value:19,description:"Fixed AC: 19 (18 base + 1 enhancement)"},{type:"special_property",target:"stealth_disadvantage",value:!0,description:"Stealth disadvantage"},{type:"stat_requirement",target:"STR",value:15,description:"Requires STR 15"}],spawnWeight:.1,source:"custom",tags:["magic","armor","heavy","enhanced"]},{name:"Elven Chain",type:"armor",rarity:"rare",weight:20,acBonus:16,description:"Exquisitely crafted elven armor that functions as light armor for all purposes. Can be worn even by those without armor proficiency and makes no sound when moved.",properties:[{type:"passive_modifier",target:"ac",value:16,description:"Fixed AC: 16"},{type:"special_property",target:"armor_classification",value:"light",description:"Counts as light armor for all features"},{type:"special_property",target:"stealth_normal",value:!0,description:"No stealth disadvantage"},{type:"special_property",target:"no_proficiency_required",value:!0,description:"Can be worn without armor proficiency"}],spawnWeight:.05,source:"custom",tags:["magic","armor","elven","versatile"]},{name:"Belt of Giant Strength (Hill Giant)",type:"item",rarity:"rare",weight:1,description:"A belt woven from giant hair that grants the strength of a hill giant. While worn, your Strength score becomes 21, regardless of your actual strength.",properties:[{type:"stat_bonus",target:"STR",value:21,description:"Strength score becomes 21 while worn",condition:{type:"while_equipped",value:!0},stackable:!1}],spawnWeight:.15,source:"custom",tags:["magic","wondrous","strength"]},{name:"Amulet of Proof Against Detection",type:"item",rarity:"uncommon",weight:.1,description:"A protective amulet that hides you from divination magic. Makes you difficult to detect through magical means and grants some protection against spells.",properties:[{type:"ability_unlock",target:"anti_magic_detection",value:!0,description:"Hidden from divination magic"},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to saving throws against spells"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","anti_detection"]},{name:"Headband of Intellect",type:"item",rarity:"uncommon",weight:.1,description:"A forehead band embroidered with intellectual symbols. While worn, your Intelligence score becomes 19, making you brilliant regardless of your natural intellect.",properties:[{type:"stat_bonus",target:"INT",value:19,description:"Intelligence score becomes 19 while worn",stackable:!1}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","intelligence"]},{name:"Boots of Elvenkind",type:"item",rarity:"uncommon",weight:1,description:"Silent magical boots that make you an expert at stealth. Your steps make no sound, and you gain advantage or bonuses to Stealth checks.",grantsSkills:[{skillId:"stealth",level:"expertise"}],properties:[{type:"special_property",target:"silent_steps",value:!0,description:"Make no noise when walking"},{type:"passive_modifier",target:"stealth_check",value:1,description:"+1 to Stealth checks"}],spawnWeight:.3,source:"custom",tags:["magic","wondrous","boots","stealth","elven"]},{name:"Gloves of Thievery",type:"item",rarity:"uncommon",weight:.1,description:"Magical gloves that make you a master of larceny. Grants expertise with thieves' tools and proficiency in sleight of hand.",grantsSkills:[{skillId:"sleight_of_hand",level:"proficient"}],properties:[{type:"passive_modifier",target:"sleight_of_hand",value:2,description:"+2 to Sleight of Hand checks"},{type:"special_property",target:"thieves_tools_expertise",value:!0,description:"Advantage on checks using thieves' tools"}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","thieves_tools"]},{name:"Boots of Speed",type:"item",rarity:"rare",weight:1,description:"Magical boots that dramatically increase your speed and grant freedom of movement. You can dash as a bonus action and leave no trace when you walk.",properties:[{type:"passive_modifier",target:"speed",value:10,description:"+10 walking speed"}],grantsFeatures:[{id:"boots_of_speed_dash",name:"Speed Burst",description:"You can take the Dash action as a bonus action, and moving leaves no trace.",effects:[{type:"special_property",target:"bonus_action_dash",value:!0,description:"Dash as bonus action"}],source:"equipment_inline"},{id:"boots_of_speed_freedom",name:"Unfettered Movement",description:"Your movement is unimpeded by difficult terrain, and spells and effects can neither reduce your speed nor cause you to be restrained.",effects:[{type:"special_property",target:"ignore_difficult_terrain",value:!0,description:"Ignore difficult terrain"},{type:"special_property",target:"freedom_of_movement",value:!0,description:"Immunity to speed reduction and restraint"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","wondrous","boots","speed"]},{name:"Boots of Striding and Springing",type:"item",rarity:"uncommon",weight:1,description:"Magical boots that enhance your mobility. Move faster, jump farther, and require less running start for long and high jumps.",properties:[{type:"passive_modifier",target:"walking_speed",value:10,description:"+10 walking speed"},{type:"passive_modifier",target:"jump_distance",value:3,description:"Triple jump distance"},{type:"ability_unlock",target:"standing_long_jump",value:!0,description:"Can run, long jump, and high jump with only 10 feet of running start"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","boots","movement","jumping"]},{name:"Boots of Flying",type:"item",rarity:"rare",weight:1,description:"Magical boots that grant the power of flight. You can fly at will as a bonus action, though overuse temporarily exhausts their magic.",properties:[{type:"ability_unlock",target:"fly_speed",value:60,description:"Fly at 60 feet per round"}],grantsFeatures:[{id:"boots_of_flying_power",name:"Flight",description:"You can fly as a bonus action. The boots lose this power for 12 hours if you use it for more than 4 hours in a 24 hour period.",effects:[{type:"ability_unlock",target:"fly_speed",value:60,description:"Fly at 60 feet"}],source:"equipment_inline"}],spawnWeight:.1,source:"custom",tags:["magic","wondrous","boots","flight"]},{name:"Ring of Protection",type:"item",rarity:"rare",weight:.1,description:"A protective ring that enhances your defense. Grants a bonus to Armor Class and all saving throws, stacking with other protective items.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to all saving throws",stackable:!0}],spawnWeight:.2,source:"custom",tags:["magic","ring","defense","ac","saves"]},{name:"Amulet of Proof Against Poison",type:"item",rarity:"uncommon",weight:.1,description:"A protective amulet that grants complete immunity to poison. Protects against both poison damage and the poisoned condition.",properties:[{type:"passive_modifier",target:"immunity_poison",value:!0,description:"Immunity to poison damage"},{type:"special_property",target:"condition_immunity",value:"poisoned",description:"Immune to poisoned condition"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","poison_immunity","defense"]},{name:"Cloak of Protection",type:"item",rarity:"uncommon",weight:1,description:"A magical cloak that wraps you in protective energy. Grants bonuses to Armor Class and saving throws that stack with rings of protection.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to all saving throws",stackable:!0}],spawnWeight:.25,source:"custom",tags:["magic","wondrous","cloak","defense","ac","saves"]},{name:"Goggles of Night",type:"item",rarity:"uncommon",weight:.1,description:"Magical lenses that grant darkvision. You can see in darkness as if it were dim light, out to a range of 60 feet.",properties:[{type:"ability_unlock",target:"darkvision",value:60,description:"Darkvision 60 feet"}],spawnWeight:.3,source:"custom",tags:["magic","wondrous","vision","darkvision"]},{name:"Lantern of Revealing",type:"item",rarity:"uncommon",weight:2,description:"A magical lantern that reveals invisible creatures and objects. Sheds light in a 60-foot radius and exposes anything hidden by illusion magic.",properties:[{type:"special_property",target:"reveal_invisible",value:30,description:"Reveal invisible creatures and objects within 30ft"},{type:"special_property",target:"light",value:"bright_light_30ft_dim_30ft",description:"Sheds bright light 30ft, dim light 30ft beyond"}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","light","detection"]},{name:"Ring of Spell Storing",type:"item",rarity:"rare",weight:.1,description:"A magical ring that can store spells for later use. The caster places spells into the ring, and any creature can cast them regardless of class.",properties:[{type:"special_property",target:"spell_storage",value:5,description:"Store up to 5 levels of spells"}],grantsSpells:[{spellId:"stored_spell",level:1,uses:1,recharge:"short_rest"}],spawnWeight:.15,source:"custom",tags:["magic","ring","spell_storage"]},{name:"Pearl of Power (3rd Level)",type:"item",rarity:"uncommon",weight:.1,description:"A small pearl that glitters with magical energy. Once per day, you can use it to recover a spent 3rd level spell slot.",properties:[{type:"special_property",target:"spell_slot_recovery",value:3,description:"Recover one expended 3rd level spell slot",condition:{type:"custom",value:"once_per_day",description:"Once per day at dawn"}}],spawnWeight:.2,source:"custom",tags:["magic","wondrous","spell_recovery","pearl"]},{name:"Wand of Magic Missiles",type:"item",rarity:"uncommon",weight:1,description:"A wand that fires unerring magic missiles. Contains 7 charges that refresh each dawn at sunrise.",properties:[{type:"special_property",target:"spell_charges",value:7,description:"7 charges, expends 1 per use"}],grantsSpells:[{spellId:"magic_missile",level:1,uses:7,recharge:"dawn"}],damage:{dice:"1d4+1",damageType:"force"},spawnWeight:.2,source:"custom",tags:["magic","wand","evocation","force"]},{name:"-1 Cursed Sword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A maliciously enchanted sword that appears magical but impairs your combat abilities. Once attuned, it cannot be removed without breaking the curse.",properties:[{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 penalty to attack and damage rolls"},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Once attuned, cannot remove unless targeted by remove curse"}],spawnWeight:.05,source:"custom",tags:["magic","weapon","cursed","curse","negative"]},{name:"Belt of Strength Drain (Cursed)",type:"item",rarity:"uncommon",weight:1,description:"A cursed belt that appears beneficial but actually drains your strength. Once donned, it cannot be removed until the curse is broken.",properties:[{type:"stat_bonus",target:"STR",value:-4,description:"-4 Strength while worn",stackable:!0},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Appears as Belt of Giant Strength until donned"}],spawnWeight:0,source:"custom",tags:["magic","cursed","curse","debuff"]},{name:"Helmet of Opposite Alignment (Cursed)",type:"item",rarity:"rare",weight:3,description:"A cursed helmet that slowly corrupts your alignment, turning you toward your moral opposite. Only detectable when it's too late.",properties:[{type:"special_property",target:"alignment_change",value:"opposite",description:"Changes character alignment to opposite",condition:{type:"on_damage_taken",value:!0}},{type:"special_property",target:"curse_attunement",value:!0,description:"Cursed: Must be attuned to remove, changes alignment"}],spawnWeight:0,source:"custom",tags:["magic","cursed","curse","alignment","helmet"]},{name:"Moon Sickle",type:"weapon",rarity:"rare",weight:2,damage:{dice:"1d4",damageType:"slashing",versatile:"1d6"},weaponProperties:["finesse","light","thrown"],description:"A druidic sickle blessed by moonlight. Deals extra radiant damage at night and gleams with silvery light when the moon is visible.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"1d6",description:"+1d6 radiant damage at night",condition:{type:"at_time_of_day",value:"night"}}],spawnWeight:.1,source:"custom",tags:["magic","weapon","finesse","light","moon","radiant"]},{name:"Sun Blade",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"radiant",versatile:"1d10"},weaponProperties:["finesse","versatile"],description:"A brilliant blade that thrives in sunlight but weakens in darkness. Deals devastating radiant damage during the day but becomes clumsy at night.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"1d8",description:"+1d8 radiant damage in daylight",condition:{type:"at_time_of_day",value:"day"}},{type:"special_property",target:"light",value:"bright_light_20ft_dim_20ft",description:"Sheds bright light 20ft, dim 20ft"},{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 to attack rolls at night",condition:{type:"at_time_of_day",value:"night"}}],spawnWeight:.05,source:"custom",tags:["magic","weapon","sun","radiant","light"]},{name:"Dwarf-Forged Armor",type:"armor",rarity:"rare",weight:40,acBonus:15,description:"Magically crafted dwarven armor that only works properly for dwarf characters. Grants additional protection and saving throw bonuses to its intended users.",properties:[{type:"passive_modifier",target:"ac",value:15,description:"Base AC: 15 + DEX (max 2)"},{type:"passive_modifier",target:"ac",value:2,description:"+2 AC for dwarves only",condition:{type:"wielder_race",value:"Dwarf"}},{type:"passive_modifier",target:"saving_throws",value:1,description:"+1 to saving throws for dwarves",condition:{type:"wielder_race",value:"Dwarf"}}],spawnWeight:.1,source:"custom",tags:["magic","armor","medium","dwarven"]},{name:"Wizard's Staff",type:"weapon",rarity:"uncommon",weight:4,damage:{dice:"1d6",damageType:"bludgeoning",versatile:"1d8"},weaponProperties:["versatile","two-handed"],description:"An arcane staff that enhances magical abilities for wizards. Grants bonuses to spell attacks and save DC, but only functions for wizards.",properties:[{type:"passive_modifier",target:"spell_attack",value:1,description:"+1 to spell attack rolls for wizards",condition:{type:"wielder_class",value:"Wizard"}},{type:"passive_modifier",target:"spell_save_dc",value:1,description:"+1 to spell save DC for wizards",condition:{type:"wielder_class",value:"Wizard"}}],spawnWeight:.2,source:"custom",tags:["magic","weapon","staff","wizard"]},{name:"Flaming Longsword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"slashing",versatile:"1d10"},weaponProperties:["finesse","versatile"],templateId:"flaming_weapon_template",description:"A longsword enchanted with fiery magic. Burns with magical flame and sheds light when its power is activated.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light 20ft"}],spawnWeight:.15,source:"custom",tags:["magic","weapon","fire","flaming","template"]},{name:"Frost Longsword",type:"weapon",rarity:"rare",weight:3,damage:{dice:"1d8",damageType:"cold",versatile:"1d10"},weaponProperties:["finesse","versatile"],templateId:"frost_weapon_template",description:"A longsword enchanted with icy magic. Freezes with cold power and can extinguish flames when wielded.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage",condition:{type:"on_hit",value:!0}}],spawnWeight:.15,source:"custom",tags:["magic","weapon","cold","frost","template"]}],_={WEAPON_ENCHANTMENTS:{plusOne:{id:"plus_one",name:"+1 Enhancement",description:"A magical enhancement that improves the weapon's accuracy and striking power. The weapon becomes supernaturally sharp and well-balanced, striking true more often and cutting deeper.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusTwo:{id:"plus_two",name:"+2 Enhancement",description:"A potent magical enhancement that significantly improves the weapon's combat effectiveness. The weapon hums with arcane energy, seeking its target with unerring precision and delivering devastating blows.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusThree:{id:"plus_three",name:"+3 Enhancement",description:"An exceptionally powerful magical enhancement that transforms the weapon into an instrument of legend. The weapon pulses with visible arcane energy, striking with supernatural force and nearly flawless accuracy.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},flaming:{id:"flaming",name:"Flaming",description:"The weapon bursts into flame upon command, dealing additional fire damage and illuminating the surrounding area. The flames do not harm the wielder but sear foes with each strike. Ancient runes of fire glow along the blade when activated.",properties:[{type:"damage_bonus",target:"fire_damage",value:"1d6",description:"+1d6 fire damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"light",value:"bright_light_20ft",description:"Sheds bright light in a 20ft radius and dim light for 20ft beyond"}],appliedAt:new Date().toISOString(),source:"enchantment"},frost:{id:"frost",name:"Frost",description:"The weapon is sheathed in supernatural cold, freezing whatever it touches. A pale white aura surrounds the blade, and each strike leaves frost on the target's wound, slowing them with supernatural chill.",properties:[{type:"damage_bonus",target:"cold_damage",value:"1d6",description:"+1d6 cold damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},shocking:{id:"shocking",name:"Shocking",description:"Crackling arcs of lightning dance across this weapon, delivering painful electrical shocks to anyone it strikes. The air around the weapon hums with ozone, and blue sparks fly with each impact.",properties:[{type:"damage_bonus",target:"lightning_damage",value:"1d6",description:"+1d6 lightning damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},thundering:{id:"thundering",name:"Thundering",description:"Each strike from this weapon is accompanied by a deafening thunderclap that can be heard for hundreds of feet. The concussion from impacts can knock foes back and leave them disoriented from the deafening noise.",properties:[{type:"damage_bonus",target:"thunder_damage",value:"1d6",description:"+1d6 thunder damage on hit",condition:{type:"on_hit",value:!0}},{type:"special_property",target:"thunderous_strike",value:!0,description:"Creates a thunderous clap on hit"}],appliedAt:new Date().toISOString(),source:"enchantment"},acidic:{id:"acidic",name:"Acidic",description:"The weapon drips with corrosive acid that continues to eat away at armor and flesh after each strike. Dark green ichor coats the blade, hissing as it melts through defenses and continues burning the wound.",properties:[{type:"damage_bonus",target:"acid_damage",value:"1d6",description:"+1d6 acid damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},poison:{id:"poison",name:"Poisoned",description:"A sinister coating of supernatural poison coats this weapon, weakening foes with every wound. The poison is distilled from venomous creatures and dark alchemy, causing progressive weakness in those struck.",properties:[{type:"damage_bonus",target:"poison_damage",value:"1d6",description:"+1d6 poison damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},holy:{id:"holy",name:"Holy",description:"Consecrated by divine rites, this weapon glows with holy light that burns the unholy. Radiant energy swirls around the blade, dealing extra damage to creatures of darkness and evil while leaving the wielder untouched.",properties:[{type:"damage_bonus",target:"radiant_damage",value:"1d6",description:"+1d6 radiant damage on hit",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},vampiric:{id:"vampiric",name:"Vampiric",description:"This cursed weapon draws vitality from those it strikes, transferring their life force to the wielder. Dark red veins pulse along the blade as it feeds, and the wielder feels a rush of healing energy with each killing blow.",properties:[{type:"special_property",target:"life_steal",value:"1d6",description:"Regain 1d6 HP when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"},vorpalEdge:{id:"vorpal_edge",name:"Vorpal Edge",description:"The edge of this weapon is supernaturally sharp, capable of striking致命 wounds with remarkable frequency. The blade seems to seek vital points on its own, turning glancing blows into devastating critical hits.",properties:[{type:"special_property",target:"expanded_crit_range",value:19,description:"Critical hits on 19-20"}],appliedAt:new Date().toISOString(),source:"enchantment"},keenEdge:{id:"keen_edge",name:"Keen Edge",description:"This weapon has been honed to perfection by magical means, its edge so impossibly sharp that it can slip through the smallest gaps in armor. Every swing has a chance to strike a致命 point, making even near-misses into devastating hits.",properties:[{type:"special_property",target:"expanded_crit_range",value:18,description:"Critical hits on 18-20"}],appliedAt:new Date().toISOString(),source:"enchantment"},mighty:{id:"mighty",name:"Mighty",description:"Enchanted to strike with exceptional force, this weapon deals more damage with each blow. The weapon feels unnaturally heavy and solid in the hand, transferring more kinetic energy into each impact for crushing wounds.",properties:[{type:"special_property",target:"damage_die_increase",value:1,description:"Weapon damage dice increased by one step"}],appliedAt:new Date().toISOString(),source:"enchantment"},returning:{id:"returning",name:"Returning",description:"This thrown weapon magically returns to the wielder's hand immediately after each attack. An invisible tether connects weapon to wielder, pulling it back through the air regardless of obstacles or distance.",properties:[{type:"special_property",target:"returns_to_hand",value:!0,description:"Weapon returns to wielder's hand immediately after being thrown"}],appliedAt:new Date().toISOString(),source:"enchantment"},lifestealing:{id:"lifestealing",name:"Lifestealing",description:"A powerful variant of vampiric enchantment that drains significantly more vitality from victims. The weapon pulses with dark crimson energy that gushes forth with each strike, healing the wielder for substantial amounts.",properties:[{type:"special_property",target:"life_steal",value:"2d6",description:"Regain 2d6 HP when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"enchantment"}},ARMOR_ENCHANTMENTS:{plusOne:{id:"plus_one_armor",name:"+1 Armor Enhancement",description:"A magical enhancement that makes the armor supernaturally resistant to blows without adding weight. The material seems to harden when struck, turning aside attacks that would have found purchase in mundane armor.",properties:[{type:"passive_modifier",target:"ac",value:1,description:"+1 Armor Class",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"},plusTwo:{id:"plus_two_armor",name:"+2 Armor Enhancement",description:"A potent magical enhancement that significantly improves the armor's protective qualities. The armor gleams with protective enchantments, and blows that connect seem to strike an invisible barrier just beneath the surface.",properties:[{type:"passive_modifier",target:"ac",value:2,description:"+2 Armor Class",stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}},RESISTANCE_ENCHANTMENTS:{fire:{id:"fire_resistance",name:"Fire Resistance",description:"The wearer is protected from fire and heat, as if continually under the effects of a protection spell. Flames lick harmlessly at the armor, and the wearer can walk through infernos that would incinerate ordinary mortals.",properties:[{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},cold:{id:"cold_resistance",name:"Cold Resistance",description:"Magical warmth protects the wearer from the bitterest cold. Frost cannot form on the armor, and the wearer remains comfortable in temperatures that would freeze others solid, protected by an unseen aura of heat.",properties:[{type:"passive_modifier",target:"resistance_cold",value:!0,description:"Resistance to cold damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},lightning:{id:"lightning_resistance",name:"Lightning Resistance",description:"The armor conducts electricity harmlessly away from the wearer, dispersing lightning strikes across its surface. Lightning bolts crackle harmlessly across the enchanted metal, leaving the wearer untouched.",properties:[{type:"passive_modifier",target:"resistance_lightning",value:!0,description:"Resistance to lightning damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},acid:{id:"acid_resistance",name:"Acid Resistance",description:"The armor is protected from corrosion by potent magical wards. Acid sizzles harmlessly on the surface without eating through the metal, and the wearer is protected from splashes of corrosive substances.",properties:[{type:"passive_modifier",target:"resistance_acid",value:!0,description:"Resistance to acid damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},poison:{id:"poison_resistance",name:"Poison Resistance",description:"Magical cleansing properties protect the wearer from toxins and venoms. Poisonous fumes and coated weapons cannot harm the wearer, as the enchantment neutralizes poisons before they can take effect.",properties:[{type:"passive_modifier",target:"resistance_poison",value:!0,description:"Resistance to poison damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},necrotic:{id:"necrotic_resistance",name:"Necrotic Resistance",description:"Dark protective wards shield the wearer from the draining touch of undeath and necrotic energies. The armor glimmers faintly with opposing light that pushes back against death magic and life-draining attacks.",properties:[{type:"passive_modifier",target:"resistance_necrotic",value:!0,description:"Resistance to necrotic damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},radiant:{id:"radiant_resistance",name:"Radiant Resistance",description:"The armor can absorb and disperse divine and radiant energy without harm. Holy fire and celestial beams wash over the wearer like water, their blinding intensity reduced to manageable levels.",properties:[{type:"passive_modifier",target:"resistance_radiant",value:!0,description:"Resistance to radiant damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},thunder:{id:"thunder_resistance",name:"Thunder Resistance",description:"The wearer is protected from sonic damage and concussive force. Thunderous impacts and deafening blasts are dampened by the armor, which absorbs and disperses the energy harmlessly.",properties:[{type:"passive_modifier",target:"resistance_thunder",value:!0,description:"Resistance to thunder damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},all:{id:"all_resistance",name:"Universal Resistance",description:"An incredibly powerful enchantment that provides resistance to all forms of damage. The armor seems to exist partially out of phase with reality, causing attacks of all types to glance off or pass harmlessly through.",properties:[{type:"passive_modifier",target:"resistance_all",value:!0,description:"Resistance to all damage"}],appliedAt:new Date().toISOString(),source:"enchantment"}},COMBO_ENCHANTMENTS:{holyAvenger:{id:"holy_avenger",name:"Holy Avenger",description:"The ultimate weapon of a paladin, consecrated to destroy evil. This blade glows with blinding holy light when wielded by a righteous warrior, dealing devastating damage to fiends and undead while protecting the wielder from dark magic.",properties:[{type:"passive_modifier",target:"attack_roll",value:3,description:"+3 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage vs fiends and undead",condition:{type:"vs_creature_type",value:"fiend"}},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage vs fiends and undead",condition:{type:"vs_creature_type",value:"undead"}},{type:"passive_modifier",target:"saving_throws",value:5,description:"+5 to saving throws vs spells"}],appliedAt:new Date().toISOString(),source:"enchantment"},dragonSlayer:{id:"dragon_slayer",name:"Dragon Slayer",description:"Forged specifically to hunt the greatest of beasts, this weapon pulses with dragon-slaying magic. Runes of ancient dragon hunters glow when chromatic or metallic dragons are near, and the blade strikes true against their armored scales.",properties:[{type:"passive_modifier",target:"attack_roll",value:2,description:"+2 to attack and damage rolls"},{type:"damage_bonus",target:"dragon_damage",value:"3d6",description:"+3d6 damage against dragons",condition:{type:"vs_creature_type",value:"dragon"}},{type:"passive_modifier",target:"resistance_fire",value:!0,description:"Resistance to fire damage"}],appliedAt:new Date().toISOString(),source:"enchantment"},demonHunter:{id:"demon_hunter",name:"Demon Hunter",description:"Blessed by celestial powers for the specific purpose of hunting fiends. The weapon bears holy symbols that burn with cold fire when in the presence of demons, causing extra pain to creatures of the lower planes.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"fiend_damage",value:"2d6",description:"+2d6 damage against fiends",condition:{type:"vs_creature_type",value:"fiend"}}],appliedAt:new Date().toISOString(),source:"enchantment"},undeadBane:{id:"undead_bane",name:"Undead Bane",description:"Consecrated to disrupt the unnatural forces that animate the dead. The weapon glows with positive energy that burns the undead, and its touch can momentarily restore peace to tormented spirits even as it destroys their forms.",properties:[{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls"},{type:"damage_bonus",target:"radiant_damage",value:"2d6",description:"+2d6 radiant damage against undead",condition:{type:"vs_creature_type",value:"undead"}}],appliedAt:new Date().toISOString(),source:"enchantment"}},CURSES:{minusOne:{id:"minus_one",name:"Cursed: -1 Penalty",description:"A malevolent enchantment that subtly sabotages the wielder's combat ability. The weapon feels slightly off-balance or slippery, causing blows to miss their mark or strike with reduced force despite the wielder's best efforts.",properties:[{type:"passive_modifier",target:"attack_roll",value:-1,description:"-1 penalty to attack and damage rolls"}],appliedAt:new Date().toISOString(),source:"curse"},minusTwo:{id:"minus_two",name:"Cursed: -2 Penalty",description:"A stronger version of the penalty curse that significantly impairs the wielder's fighting ability. The weapon actively fights against being used effectively, as if it has a will of its own that opposes its owner.",properties:[{type:"passive_modifier",target:"attack_roll",value:-2,description:"-2 penalty to attack and damage rolls"}],appliedAt:new Date().toISOString(),source:"curse"},weakness:{id:"weakness",name:"Cursed: Weakness",description:"The armor or weapon saps the wearer's physical strength, leaving them feeling constantly exhausted and feeble. Muscles seem to wither under its influence, and once-trivial feats of strength become impossibly difficult.",properties:[{type:"stat_bonus",target:"STR",value:-4,description:"-4 Strength while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},feeblemind:{id:"feeblemind",name:"Cursed: Feeblemind",description:"A maddening curse that clouds the mind and disrupts logical thought. The wearer struggles with memory, reasoning, and mental clarity, as if a thick fog has settled over their intellect.",properties:[{type:"stat_bonus",target:"INT",value:-4,description:"-4 Intelligence while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},clumsiness:{id:"clumsiness",name:"Cursed: Clumsiness",description:"The cursed item disrupts the wearer's coordination and reflexes. Simple tasks become fumbling ordeals, and the wearer constantly drops things, trips over nothing, and fails at feats of agility they once performed effortlessly.",properties:[{type:"stat_bonus",target:"DEX",value:-4,description:"-4 Dexterity while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},frailty:{id:"frailty",name:"Cursed: Frailty",description:"The item drains vitality from the wearer, leaving them weak and sickly. Wounds heal slowly, diseases take hold more easily, and the wearer feels constantly exhausted as if recovering from a terrible illness.",properties:[{type:"stat_bonus",target:"CON",value:-4,description:"-4 Constitution while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},foolishness:{id:"foolishness",name:"Cursed: Foolishness",description:"The curse clouds judgment and perception, making the wearer dangerously gullible and unaware. They fail to notice obvious threats, misjudge situations completely, and lose touch with their intuitive and perceptive abilities.",properties:[{type:"stat_bonus",target:"WIS",value:-4,description:"-4 Wisdom while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},repulsiveness:{id:"repulsiveness",name:"Cursed: Repulsiveness",description:"The wearer becomes unnaturally off-putting and difficult to be around. People feel instinctively uncomfortable or repulsed in their presence, and attempts at persuasion or leadership almost always fail despite the wearer's best efforts.",properties:[{type:"stat_bonus",target:"CHA",value:-4,description:"-4 Charisma while equipped"}],appliedAt:new Date().toISOString(),source:"curse"},fireVulnerability:{id:"fire_vulnerability",name:"Cursed: Fire Vulnerability",description:"The cursed item makes the wearer supernaturally susceptible to fire. Even small flames cause agonizing burns, and the wearer takes double damage from any fire-based attack as if their very skin were coated in oil.",properties:[{type:"passive_modifier",target:"vulnerability_fire",value:!0,description:"Vulnerability to fire damage (double damage)"}],appliedAt:new Date().toISOString(),source:"curse"},coldVulnerability:{id:"cold_vulnerability",name:"Cursed: Cold Vulnerability",description:"The curse robs the wearer of all natural resistance to cold. Chilly temperatures become painfully freezing, and the wearer takes double damage from cold attacks as if they have no protection against the bitter chill.",properties:[{type:"passive_modifier",target:"vulnerability_cold",value:!0,description:"Vulnerability to cold damage (double damage)"}],appliedAt:new Date().toISOString(),source:"curse"},lifesteal:{id:"lifesteal",name:"Cursed: Bloodthirst",description:"A malevolent curse that causes the weapon to feed on its wielder's vitality. Each time the weapon strikes a foe, it also tears life force from the one who wields it, draining blood and energy in exchange for the power it grants.",properties:[{type:"special_property",target:"life_drain",value:"1d4",description:"Wielder takes 1d4 necrotic damage when dealing damage",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"curse"},attunement:{id:"attunement",name:"Cursed: Attunement Lock",description:"Once donned, this equipment refuses to be removed by any means short of powerful magic. The item fuses to the wearer's body, becoming like a second skin that cannot be stripped away no matter how desperately they wish to be free of it.",properties:[{type:"special_property",target:"curse_attunement",value:!0,description:"Once equipped, cannot be removed unless targeted by remove curse"}],appliedAt:new Date().toISOString(),source:"curse"},berserker:{id:"berserker",name:"Cursed: Berserker Rage",description:"The cursed weapon fills the wielder with uncontrollable battle fury. They must attack every round without pause or be overwhelmed by the curse's wrath, leaving them disadvantaged as the weapon punishes their hesitation.",properties:[{type:"special_property",target:"berserker_rage",value:!0,description:"Must attack each round or take disadvantage on all attacks"},{type:"passive_modifier",target:"attack_roll",value:1,description:"+1 to attack and damage rolls (while berserking)"}],appliedAt:new Date().toISOString(),source:"curse"},heavyBurden:{id:"heavy_burden",name:"Cursed: Heavy Burden",description:"The cursed equipment becomes impossibly heavy, as if made of solid lead. Every movement becomes a struggle, and the wearer moves with painful slowness under the crushing weight that only they can feel.",properties:[{type:"special_property",target:"weight_multiplier",value:2,description:"Equipment weight is doubled"},{type:"passive_modifier",target:"speed",value:-5,description:"-5 walking speed"}],appliedAt:new Date().toISOString(),source:"curse"},lightSensitivity:{id:"light_sensitivity",name:"Cursed: Light Sensitivity",description:"The cursed armor or weapon makes the wearer painfully sensitive to light. Bright daylight or magical illumination causes agonizing pain and impairs vision, leaving the wearer disadvantaged in any well-lit environment.",properties:[{type:"special_property",target:"light_sensitivity",value:!0,description:"Disadvantage on attacks and perception in bright light"}],appliedAt:new Date().toISOString(),source:"curse"},invisibility:{id:"invisibility",name:"Cursed: Invisibility",description:"A twisted version of invisibility that hides the wearer from sight but impairs their ability to interact with the world. The wearer cannot see themselves, and their own movements become uncertain and clumsy while invisible.",addsFeatures:[{id:"curse_invisibility_feature",name:"Cursed Invisibility",description:"You are invisible while equipped. However, you have disadvantage on attack rolls and enemies have advantage on attacks against you before you are revealed.",effects:[{type:"ability_unlock",target:"invisibility",value:!0,description:"Invisible"},{type:"passive_modifier",target:"attack_roll",value:-5,description:"Disadvantage on attacks while invisible"}],source:"equipment_inline"}],properties:[],appliedAt:new Date().toISOString(),source:"curse"},hallucinations:{id:"hallucinations",name:"Cursed: Hallucinations",description:"The cursed item causes terrifying hallucinations that distort reality. Allies appear as enemies, enemies as trusted friends, and the wearer cannot trust their own senses as the line between nightmare and reality blurs.",properties:[{type:"special_property",target:"hallucinations",value:"25_percent",description:"25% chance each round to see enemies as allies and vice versa"}],appliedAt:new Date().toISOString(),source:"curse"},bloodMoney:{id:"blood_money",name:"Cursed: Blood Money",description:"A cruel curse that converts violence into pain for the wielder. Every successful strike causes wounds to mysteriously appear on the wielder's body, as if the weapon siphons blood in exchange for the damage it deals.",properties:[{type:"special_property",target:"hp_on_damage",value:"1d4",description:"Wielder takes 1d4 damage when dealing damage to enemies",condition:{type:"on_hit",value:!0}}],appliedAt:new Date().toISOString(),source:"curse"}},ALL_ENCHANTMENTS:{}};_.ALL_ENCHANTMENTS.plusOne=_.WEAPON_ENCHANTMENTS.plusOne;_.ALL_ENCHANTMENTS.plusTwo=_.WEAPON_ENCHANTMENTS.plusTwo;_.ALL_ENCHANTMENTS.plusThree=_.WEAPON_ENCHANTMENTS.plusThree;_.ALL_ENCHANTMENTS.flaming=_.WEAPON_ENCHANTMENTS.flaming;_.ALL_ENCHANTMENTS.frost=_.WEAPON_ENCHANTMENTS.frost;_.ALL_ENCHANTMENTS.shocking=_.WEAPON_ENCHANTMENTS.shocking;_.ALL_ENCHANTMENTS.thundering=_.WEAPON_ENCHANTMENTS.thundering;_.ALL_ENCHANTMENTS.acidic=_.WEAPON_ENCHANTMENTS.acidic;_.ALL_ENCHANTMENTS.poison=_.WEAPON_ENCHANTMENTS.poison;_.ALL_ENCHANTMENTS.holy=_.WEAPON_ENCHANTMENTS.holy;_.ALL_ENCHANTMENTS.vampiric=_.WEAPON_ENCHANTMENTS.vampiric;_.ALL_ENCHANTMENTS.vorpalEdge=_.WEAPON_ENCHANTMENTS.vorpalEdge;_.ALL_ENCHANTMENTS.keenEdge=_.WEAPON_ENCHANTMENTS.keenEdge;_.ALL_ENCHANTMENTS.mighty=_.WEAPON_ENCHANTMENTS.mighty;_.ALL_ENCHANTMENTS.returning=_.WEAPON_ENCHANTMENTS.returning;_.ALL_ENCHANTMENTS.lifestealing=_.WEAPON_ENCHANTMENTS.lifestealing;_.ALL_ENCHANTMENTS.plusOneArmor=_.ARMOR_ENCHANTMENTS.plusOne;_.ALL_ENCHANTMENTS.plusTwoArmor=_.ARMOR_ENCHANTMENTS.plusTwo;_.ALL_ENCHANTMENTS.fire=_.RESISTANCE_ENCHANTMENTS.fire;_.ALL_ENCHANTMENTS.cold=_.RESISTANCE_ENCHANTMENTS.cold;_.ALL_ENCHANTMENTS.lightning=_.RESISTANCE_ENCHANTMENTS.lightning;_.ALL_ENCHANTMENTS.acid=_.RESISTANCE_ENCHANTMENTS.acid;_.ALL_ENCHANTMENTS.poisonResistance=_.RESISTANCE_ENCHANTMENTS.poison;_.ALL_ENCHANTMENTS.necrotic=_.RESISTANCE_ENCHANTMENTS.necrotic;_.ALL_ENCHANTMENTS.radiant=_.RESISTANCE_ENCHANTMENTS.radiant;_.ALL_ENCHANTMENTS.thunder=_.RESISTANCE_ENCHANTMENTS.thunder;_.ALL_ENCHANTMENTS.allResistance=_.RESISTANCE_ENCHANTMENTS.all;_.ALL_ENCHANTMENTS.holyAvenger=_.COMBO_ENCHANTMENTS.holyAvenger;_.ALL_ENCHANTMENTS.dragonSlayer=_.COMBO_ENCHANTMENTS.dragonSlayer;_.ALL_ENCHANTMENTS.demonHunter=_.COMBO_ENCHANTMENTS.demonHunter;_.ALL_ENCHANTMENTS.undeadBane=_.COMBO_ENCHANTMENTS.undeadBane;class be{constructor(){throw new Error("EnchantmentLibrary is a utility class and cannot be instantiated")}static getEnchantment(e){return Object.values(_.ALL_ENCHANTMENTS).find(t=>t.id===e)}static getCurse(e){return Object.values(_.CURSES).find(t=>t.id===e)}static getAllEnchantments(){return Object.values(_.ALL_ENCHANTMENTS)}static getAllCurses(){return Object.values(_.CURSES)}static getEnchantmentsByType(e){switch(e){case"weapon":return Object.values(_.WEAPON_ENCHANTMENTS);case"armor":return Object.values(_.ARMOR_ENCHANTMENTS);case"resistance":return Object.values(_.RESISTANCE_ENCHANTMENTS);case"combo":return Object.values(_.COMBO_ENCHANTMENTS);default:return[]}}static createStrengthEnchantment(e){return{id:`strength_${e}`,name:`Strength +${e}`,properties:[{type:"stat_bonus",target:"STR",value:e,description:`+${e} Strength`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createDexterityEnchantment(e){return{id:`dexterity_${e}`,name:`Dexterity +${e}`,properties:[{type:"stat_bonus",target:"DEX",value:e,description:`+${e} Dexterity`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createConstitutionEnchantment(e){return{id:`constitution_${e}`,name:`Constitution +${e}`,properties:[{type:"stat_bonus",target:"CON",value:e,description:`+${e} Constitution`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createIntelligenceEnchantment(e){return{id:`intelligence_${e}`,name:`Intelligence +${e}`,properties:[{type:"stat_bonus",target:"INT",value:e,description:`+${e} Intelligence`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createWisdomEnchantment(e){return{id:`wisdom_${e}`,name:`Wisdom +${e}`,properties:[{type:"stat_bonus",target:"WIS",value:e,description:`+${e} Wisdom`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}static createCharismaEnchantment(e){return{id:`charisma_${e}`,name:`Charisma +${e}`,properties:[{type:"stat_bonus",target:"CHA",value:e,description:`+${e} Charisma`,stackable:!0}],appliedAt:new Date().toISOString(),source:"enchantment"}}}const up=_.WEAPON_ENCHANTMENTS,dp=_.ARMOR_ENCHANTMENTS,hp=_.RESISTANCE_ENCHANTMENTS,pp=_.CURSES,mp=_.ALL_ENCHANTMENTS,gp=s=>be.getEnchantment(s),fp=s=>be.getCurse(s),yp=()=>be.getAllEnchantments(),vp=()=>be.getAllCurses(),bp=s=>be.getEnchantmentsByType(s),wp=s=>be.createStrengthEnchantment(s),Sp=s=>be.createDexterityEnchantment(s),_p=s=>be.createConstitutionEnchantment(s),Ap=s=>be.createIntelligenceEnchantment(s),kp=s=>be.createWisdomEnchantment(s),Tp=s=>be.createCharismaEnchantment(s);function Cp(s){return tt.find(e=>e.name===s)}function Ip(s){return tt.filter(e=>e.type===s)}function Ep(s){return tt.filter(e=>e.rarity===s)}function Mp(){return tt.filter(s=>s.tags?.includes("cursed"))}function Dp(s){return tt.filter(e=>e.properties?.some(t=>t.type===s))}function Rp(s,e){const t=Ft[e];return t?{...s,...t,name:`${s.name} (${e.replace("_"," ")})`,templateId:e,properties:[...s.properties||[],...t.properties||[]],tags:[...s.tags||[],...t.tags||[]]}:null}exports.ALL_GRID_TYPES=y.ALL_GRID_TYPES;exports.BEAT_ACCURACY_THRESHOLDS=y.BEAT_ACCURACY_THRESHOLDS;exports.BEAT_DETECTION_ALGORITHM=y.BEAT_DETECTION_ALGORITHM;exports.BEAT_DETECTION_VERSION=y.BEAT_DETECTION_VERSION;exports.BeatInterpolator=y.BeatInterpolator;exports.BeatMapGenerator=y.BeatMapGenerator;exports.BeatStream=y.BeatStream;exports.BeatTracker=y.BeatTracker;exports.CONSECUTIVE_KEY_LIMITS=y.CONSECUTIVE_KEY_LIMITS;exports.CompositeStreamGenerator=y.CompositeStreamGenerator;exports.DDR_PATTERN_LIBRARY=y.DDR_PATTERN_LIBRARY;exports.DEFAULT_BEATMAP_GENERATOR_OPTIONS=y.DEFAULT_BEATMAP_GENERATOR_OPTIONS;exports.DEFAULT_BEATSTREAM_OPTIONS=y.DEFAULT_BEATSTREAM_OPTIONS;exports.DEFAULT_BEAT_INTERPOLATION_OPTIONS=y.DEFAULT_BEAT_INTERPOLATION_OPTIONS;exports.DEFAULT_BUTTON_MAPPING_CONFIG=y.DEFAULT_BUTTON_MAPPING_CONFIG;exports.DEFAULT_DOWNBEAT_CONFIG=y.DEFAULT_DOWNBEAT_CONFIG;exports.DEFAULT_GROOVE_OPTIONS=y.DEFAULT_GROOVE_OPTIONS;exports.DEFAULT_RHYTHMIC_BALANCE_CONFIG=y.DEFAULT_RHYTHMIC_BALANCE_CONFIG;exports.DEFAULT_SUBDIVISION_CONFIG=y.DEFAULT_SUBDIVISION_CONFIG;exports.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS=y.DEFAULT_SUBDIVISION_PLAYBACK_OPTIONS;exports.DEFAULT_TEMPO_AWARE_CONFIG=y.DEFAULT_TEMPO_AWARE_CONFIG;exports.DEFAULT_TIME_SIGNATURE=y.DEFAULT_TIME_SIGNATURE;exports.DensityAnalyzer=y.DensityAnalyzer;exports.DifficultyVariantGenerator=y.DifficultyVariantGenerator;exports.EASY_ACCURACY_THRESHOLDS=y.EASY_ACCURACY_THRESHOLDS;exports.EASY_GROOVE_PENALTIES=y.EASY_GROOVE_PENALTIES;exports.EASY_QUARTER_NOTE_BPM=y.EASY_QUARTER_NOTE_BPM;exports.GAUSSIAN_SMOOTH_PRESETS=y.GAUSSIAN_SMOOTH_PRESETS;exports.GROOVE_PENALTY_PRESETS=y.GROOVE_PENALTY_PRESETS;exports.GROOVE_TIERS=y.GROOVE_TIERS;exports.GUITAR_HERO_PATTERN_LIBRARY=y.GUITAR_HERO_PATTERN_LIBRARY;exports.HARD_ACCURACY_THRESHOLDS=y.HARD_ACCURACY_THRESHOLDS;exports.HARD_GROOVE_PENALTIES=y.HARD_GROOVE_PENALTIES;exports.HARD_RESTRICT_BPM=y.HARD_RESTRICT_BPM;exports.HOP_SIZE_PRESETS=y.HOP_SIZE_PRESETS;exports.HighBpmGridRestrictionRule=y.HighBpmGridRestrictionRule;exports.MAX_BEATS_PER_MEASURE=y.MAX_BEATS_PER_MEASURE;exports.MAX_SUBDIVISION_DENSITY=y.MAX_SUBDIVISION_DENSITY;exports.MEDIUM_ACCURACY_THRESHOLDS=y.MEDIUM_ACCURACY_THRESHOLDS;exports.MEDIUM_GROOVE_PENALTIES=y.MEDIUM_GROOVE_PENALTIES;exports.MEDIUM_RESTRICT_BPM=y.MEDIUM_RESTRICT_BPM;exports.MEL_BANDS_PRESETS=y.MEL_BANDS_PRESETS;exports.MIN_BEATS_PER_MEASURE=y.MIN_BEATS_PER_MEASURE;exports.MultiBandAnalyzer=y.MultiBandAnalyzer;exports.OnsetStrengthEnvelope=y.OnsetStrengthEnvelope;exports.PhraseAnalyzer=y.PhraseAnalyzer;exports.PitchDetector=y.PitchDetector;exports.RHYTHM_PRESETS=y.RHYTHM_PRESETS;exports.RhythmGenerator=y.RhythmGenerator;exports.RhythmQuantizer=y.RhythmQuantizer;exports.RhythmicBalancer=y.RhythmicBalancer;exports.SUBDIVISION_LIMITS=y.SUBDIVISION_LIMITS;exports.SpectrumScanner=y.SpectrumScanner;exports.StreamScorer=y.StreamScorer;exports.TempoAwareQuantizer=y.TempoAwareQuantizer;exports.TempoDetector=y.TempoDetector;exports.TransientDetector=y.TransientDetector;exports.VALID_SUBDIVISION_TYPES=y.VALID_SUBDIVISION_TYPES;exports.calculateBeatInMeasure=y.calculateBeatInMeasure;exports.calculateMaxAchievableDensity=y.calculateMaxAchievableDensity;exports.calculateStdDev=y.calculateStdDev;exports.chartedBeatMapToBeatMap=y.chartedBeatMapToBeatMap;exports.convertToAllowedGridType=y.convertToAllowedGridType;exports.createMelFilterbank=y.createMelFilterbank;exports.deriveAllowedGridTypes=y.deriveAllowedGridTypes;exports.deriveSeed=y.deriveSeed;exports.findActiveSegment=y.findActiveSegment;exports.gaussianSmooth=y.gaussianSmooth;exports.generateSeed=y.generateSeed;exports.getAccuracyThresholdsForPreset=y.getAccuracyThresholdsForPreset;exports.getAllowedGridTypes=y.getAllowedGridTypes;exports.getConsecutiveKeyLimit=y.getConsecutiveKeyLimit;exports.getControllerModeBalanceDefaults=y.getControllerModeBalanceDefaults;exports.getControllerModeScoringDefaults=y.getControllerModeScoringDefaults;exports.getGaussianSmoothMs=y.getGaussianSmoothMs;exports.getGroovePenaltiesForPreset=y.getGroovePenaltiesForPreset;exports.getGrooveTier=y.getGrooveTier;exports.getGrooveWindowMs=y.getGrooveWindowMs;exports.getHopSizeMs=y.getHopSizeMs;exports.getMelBands=y.getMelBands;exports.getMetricGroupSize=y.getMetricGroupSize;exports.getMinHotnessForTier=y.getMinHotnessForTier;exports.getPatternById=y.getPatternById;exports.getPatternLibrary=y.getPatternLibrary;exports.getPatternLibraryStats=y.getPatternLibraryStats;exports.getPatternsByCategory=y.getPatternsByCategory;exports.getPatternsByDifficulty=y.getPatternsByDifficulty;exports.getPatternsByKeyCount=y.getPatternsByKeyCount;exports.getPatternsByTags=y.getPatternsByTags;exports.getRandomPattern=y.getRandomPattern;exports.getRhythmPreset=y.getRhythmPreset;exports.getRhythmPresetNames=y.getRhythmPresetNames;exports.getSubdivisionDensity=y.getSubdivisionDensity;exports.getTempoAwareAllowedGridTypes=y.getTempoAwareAllowedGridTypes;exports.hashSeedToFloat=y.hashSeedToFloat;exports.hashSeedToInt=y.hashSeedToInt;exports.highPassFilter=y.highPassFilter;exports.hzToMel=y.hzToMel;exports.isFullBeatMapExportData=y.isFullBeatMapExportData;exports.isGridTypeAllowed=y.isGridTypeAllowed;exports.isLevelPackExport=y.isLevelPackExport;exports.isMetricStrongBeat=y.isMetricStrongBeat;exports.isMetricWeakBeat=y.isMetricWeakBeat;exports.isStrongBeatForEmphasis=y.isStrongBeatForEmphasis;exports.isValidSubdivisionType=y.isValidSubdivisionType;exports.mapGridToSubdivision=y.mapGridToSubdivision;exports.melToHz=y.melToHz;exports.mergeButtonMappingConfig=y.mergeButtonMappingConfig;exports.naturalDifficultyToLevel=y.naturalDifficultyToLevel;exports.performBeatFFT=y.performFFT;exports.performSTFT=y.performSTFT;exports.reapplyDownbeatConfig=y.reapplyDownbeatConfig;exports.resampleAudio=y.resampleAudio;exports.unifyBeatMap=y.unifyBeatMap;exports.validateButtonMappingConfig=y.validateButtonMappingConfig;exports.validateDownbeatConfig=y.validateDownbeatConfig;exports.validateDownbeatConfigAgainstBeats=y.validateDownbeatConfigAgainstBeats;exports.validateSubdivisionConfig=y.validateSubdivisionConfig;exports.validateSubdivisionConfigAgainstBeats=y.validateSubdivisionConfigAgainstBeats;exports.validateSubdivisionDensity=y.validateSubdivisionDensity;exports.validateSubdivisionLimits=y.validateSubdivisionLimits;exports.validateThresholds=y.validateThresholds;exports.validateTrackMatch=y.validateTrackMatch;exports.ArweaveGatewayManager=z.ArweaveGatewayManager;exports.DEFAULT_GATEWAYS=z.DEFAULT_GATEWAYS;exports.KNOWN_GATEWAY_HOSTS=z.KNOWN_GATEWAY_HOSTS;exports.LogLevel=z.LogLevel;exports.Logger=z.Logger;exports.arweaveGatewayManager=z.arweaveGatewayManager;exports.constructGatewayUrl=z.constructGatewayUrl;exports.createLogger=z.createLogger;exports.getAllGatewayUrls=z.getAllGatewayUrls;exports.isArweaveUrl=z.isArweaveUrl;exports.parseArweaveUrl=z.parseArweaveUrl;exports.DEFAULT_IPFS_GATEWAY=B.DEFAULT_IPFS_GATEWAY;exports.KNOWN_IPFS_GATEWAY_HOSTS=B.KNOWN_IPFS_GATEWAY_HOSTS;exports.MetadataExtractor=B.MetadataExtractor;exports.extractIPFSPath=B.extractIPFSPath;exports.getArtists=B.getArtists;exports.getAudioUrls=B.getAudioUrls;exports.getFullTracks=B.getFullTracks;exports.getGenres=B.getGenres;exports.getImageUrls=B.getImageUrls;exports.getTags=B.getTags;exports.getTotalDuration=B.getTotalDuration;exports.getTrackCount=B.getTrackCount;exports.getTrackTitles=B.getTrackTitles;exports.getTracks=B.getTracks;exports.getVRMTracks=B.getVRMTracks;exports.getVRMs=B.getVRMs;exports.isIPFS=B.isIPFS;exports.resolveIPFSLink=B.resolveIPFSLink;exports.AICombatRunner=_s;exports.ALL_CLASSES=Ds;exports.ALL_ENCHANTMENTS=mp;exports.ALL_RACES=Ps;exports.ARMOR_ENCHANTMENTS=dp;exports.AbilityScoreCalculator=Et;exports.AbilityScoresSchema=ma;exports.AppearanceGenerator=Nn;exports.AttackResolver=ue;exports.AudioProfileSchema=Yd;exports.BASE_PLAYS_THRESHOLD=hs;exports.BASE_XP_THRESHOLD=ps;exports.BalanceValidator=ro;exports.BalancedStrategy=Bn;exports.BeatSubdivider=Qe;exports.BoxOpener=qn;exports.CLASS_AUDIO_PREFERENCES=Rs;exports.CLASS_DATA=Ce;exports.CLASS_SPELL_LISTS=Rt;exports.CLASS_STARTING_EQUIPMENT=ba;exports.CR_TO_XP=Mt;exports.CURSES=pp;exports.CharacterGenerator=Vt;exports.CharacterSheetSchema=Zd;exports.CharacterUpdater=Gh;exports.ClassSuggester=Bt;exports.ColorExtractor=Mh;exports.CombatAI=gs;exports.CombatEngine=ws;exports.CombatMetricsTracker=Ss;exports.CombatSimulator=pt;exports.CombatantAccumulator=Hs;exports.ComparativeAnalyzer=lo;exports.DEFAULT_CLASS_FEATURES=ga;exports.DEFAULT_ENEMY_TEMPLATES=Xe;exports.DEFAULT_EQUIPMENT=Ze;exports.DEFAULT_PROGRESSION_CONFIG=Ue;exports.DEFAULT_RACE_DATA_ARRAY=$s;exports.DEFAULT_RACIAL_TRAITS=fa;exports.DEFAULT_RHYTHM_XP_CONFIG=Me;exports.DEFAULT_SENSOR_CONFIG=Jn;exports.DEFAULT_SKILLS=ya;exports.DEFAULT_SKILL_CATEGORIES=ch;exports.DiceRoller=U;exports.DifficultyCalculator=ca;exports.DnD5eSmartStrategy=On;exports.DnD5eStandardStrategy=Ln;exports.ENCHANTMENT_LIBRARY=_;exports.ENEMY_COUNT_MULTIPLIER=na;exports.EXPECTED_WIN_RATES=Pa;exports.EnchantmentLibrary=be;exports.EnemyGenerator=Ke;exports.EnvironmentalSensors=sp;exports.EquipmentEffectApplier=le;exports.EquipmentGenerator=dt;exports.EquipmentModifier=Vh;exports.EquipmentSpawnHelper=Yh;exports.EquipmentValidator=Dt;exports.ExtensionManager=R;exports.FeatureEffectApplier=ht;exports.FeatureQuery=re;exports.FeatureValidator=Fe;exports.GamingPlatformSensors=rp;exports.GrooveAnalyzer=Rh;exports.ITEM_CREATION_TEMPLATES=Ft;exports.InitiativeRoller=fs;exports.LevelUpProcessor=ke;exports.MAGIC_ITEMS=tt;exports.MASTERY_BONUS_XP=xs;exports.MASTERY_THRESHOLD=zr;exports.MAX_PRESTIGE_LEVEL=st;exports.ManualStrategy=Wn;exports.NamingEngine=Sn;exports.PRESTIGE_ROMAN_NUMERALS=ds;exports.PRESTIGE_SCALING_FACTOR=ia;exports.PROFICIENCY_BONUS=Ma;exports.ParameterSweep=oo;exports.PartyAnalyzer=Sh;exports.PlaylistParser=Eh;exports.PlaylistTrackSchema=vn;exports.PrestigeSystem=se;exports.PrimaryOnlyStrategy=Fn;exports.RACE_DATA=Gr;exports.RARITY_CONFIGS=Ca;exports.RESISTANCE_ENCHANTMENTS=hp;exports.RaceSelector=Pn;exports.RandomStrategy=Hn;exports.RhythmXPCalculator=jh;exports.SKILL_ABILITY_MAP=Wr;exports.SPELL_DATABASE=Ia;exports.SPELL_SLOTS_BY_CLASS=oa;exports.SeededDiceRoller=ks;exports.SeededRNG=Se;exports.SensorDashboard=li;exports.ServerlessPlaylistSchema=Vd;exports.SessionTracker=qh;exports.SimulationAggregator=Fs;exports.SkillAssigner=xn;exports.SkillQuery=pe;exports.SkillValidator=we;exports.SpellCaster=V;exports.SpellManager=va;exports.SpellQuery=Ie;exports.SpellValidator=De;exports.StatManager=zn;exports.SubdivisionPlaybackController=Hh;exports.TUNING_FACTORS=sr;exports.WEAPON_ENCHANTMENTS=up;exports.WeightedSelector=ye;exports.XPCalculator=ti;exports.XP_BUDGET_PER_LEVEL=ys;exports.XP_THRESHOLDS=ct;exports.applyTemplate=Rp;exports.applyTuning=nr;exports.areAppearanceDefaultsInitialized=_n;exports.areClassDefaultsInitialized=En;exports.areEquipmentDefaultsInitialized=kn;exports.areFeatureDefaultsInitialized=Mn;exports.areRaceDataDefaultsInitialized=Cn;exports.areRaceDefaultsInitialized=Tn;exports.areSkillDefaultsInitialized=Dn;exports.areSpellDefaultsInitialized=An;exports.asClass=Cs;exports.assignKeyToBeat=xh;exports.assignKeysToBeats=Nh;exports.calculateAdjustedXP=rr;exports.clearAllKeys=Lh;exports.createCharismaEnchantment=Tp;exports.createConstitutionEnchantment=_p;exports.createDexterityEnchantment=Sp;exports.createIntelligenceEnchantment=Ap;exports.createSeededRoller=As;exports.createStatIncreaseStrategy=_a;exports.createStrengthEnchantment=wp;exports.createWisdomEnchantment=kp;exports.displayEnvironmentalDiagnostics=ri;exports.displayGamingDiagnostics=oi;exports.displaySystemDashboard=Kn;exports.ensureAllDefaultsInitialized=gh;exports.ensureAppearanceDefaultsInitialized=za;exports.ensureClassDefaultsInitialized=Xa;exports.ensureEquipmentDefaultsInitialized=Ga;exports.ensureFeatureDefaultsInitialized=Ja;exports.ensureRaceDataDefaultsInitialized=In;exports.ensureRaceDefaultsInitialized=Va;exports.ensureSkillDefaultsInitialized=Rn;exports.ensureSpellDefaultsInitialized=Ot;exports.evaluateMixConditions=nh;exports.extractKeyMap=$h;exports.getAllCurses=vp;exports.getAllEnchantments=yp;exports.getAllRarities=Ts;exports.getAveragePartyLevel=bs;exports.getCRFromXP=Ta;exports.getClassData=Ea;exports.getClassSpellList=la;exports.getClassStartingEquipment=$n;exports.getCurse=fp;exports.getCursedItems=Mp;exports.getEnchantment=gp;exports.getEnchantmentsByType=bp;exports.getEncounterMultiplier=ot;exports.getFeatureQuery=Vr;exports.getHigherRarity=br;exports.getItemsWithProperty=Dp;exports.getKeyCount=Bh;exports.getMagicItem=Cp;exports.getMagicItemsByRarity=Ep;exports.getMagicItemsByType=Ip;exports.getRaceData=zt;exports.getRaceDataAsync=jr;exports.getRarityConfig=Oe;exports.getSignatureDie=vr;exports.getSkillQuery=Fr;exports.getSpellQuery=qr;exports.getSpellSlotsForClass=Da;exports.getTemplateById=gr;exports.getTemplatesByArchetype=yr;exports.getTemplatesByCategory=fr;exports.getTrackExtras=bn;exports.getTrackMetadata=Xd;exports.getUsedKeys=Fh;exports.getXPBudgetForParty=Wt;exports.getXPBudgetPerLevel=vs;exports.getXPForCR=ka;exports.hasRequiredKeys=Oh;exports.initializeAllDefaults=mh;exports.initializeAppearanceDefaults=Wa;exports.initializeClassDefaults=Za;exports.initializeEquipmentDefaults=Ua;exports.initializeFeatureDefaults=Ka;exports.initializeRaceDataDefaults=Ya;exports.initializeRaceDefaults=ja;exports.initializeSkillDefaults=Qa;exports.initializeSpellDefaults=qa;exports.isPrestigeLevel=tr;exports.isValidAIPlayStyle=ar;exports.isValidClass=ra;exports.isValidEncounterDifficulty=wh;exports.isValidEncounterDifficultyUtil=or;exports.isValidEnemyArchetype=bh;exports.isValidEnemyCategory=yh;exports.isValidEnemyRarity=vh;exports.loadConfigFromEnv=Qn;exports.mergeConfig=op;exports.mergeProgressionConfig=cp;exports.mergeRhythmXPConfig=Pt;exports.resolveSelectedMix=wn;exports.shouldAccuracyBreakCombo=po;exports.subdivideBeatMap=Ph;exports.toPrestigeLevel=ms;exports.validateClassFeature=kr;exports.validateClassFeatures=Cr;exports.validateRacialTrait=Tr;exports.validateRacialTraits=Ir;exports.validateSkill=Mr;exports.validateSkillListDefinition=xr;exports.validateSkillPrerequisites=Nr;exports.validateSkillProficiencies=Pr;exports.validateSkillProficiency=Rr;exports.validateSkills=Dr;exports.validateSpell=$r;exports.validateSpellPrerequisites=Br;exports.validateSpellPrerequisitesSchema=Or;exports.validateSpells=Lr;
|