Masterwork is a new crafting level for Rarity players and builders. Like Rarity's first crafting level, masterwork contains a crafting station and a dungeon. Use the masterwork crafting station to create masterwork weapons, armor, and tools. Defeat monsters in the dungeon for loot that speeds up crafting at the crafting station.
Masterwork is an expansion of the original Rarity core created by Andre Cronje, et al in September 2021. It continues the vision of an open, free-to-mint, d20 implementation in solidity.
Name | Address |
---|---|
rarity_adventure_2 | 0xCc60d735bc1cBE877E20212aD3A93F88Ef243c7B |
rarity_adventure_2_uri | 0x94C0FEe093Bd38e1ed5bf01aE89DBb12826aB9f4 |
rarity_masterwork_items | 0x0aF7EC3f3d17890072771e713F5DBD27D9bBc074 |
rarity_masterwork_projects | 0x969eB1901A205C3B9f299eAeB33861172f2e8165 |
rarity_masterwork_uri | 0x44500332e00E57C1ee98A7BFa3bCa0e87D3273DC |
rarity_crafting_materials_2 | 0x919a172339ffD5915686477e6Da5e0748DCf10ff |
rarity_crafting_skills | 0xc84275A99C01D0b6C1A63bD94d589e4A44a85DeD |
rarity_equipment_2 | 0xB6Ee6A99d474a30C9C407E7f32a88fF82071FDC0 |
Name | Address |
---|---|
Attributes | 0x6F222Fd2beC8105D0a843f5e0bbdc5dDD73a82b8 |
Combat | 0xf6bDcA6D83567c3934d45c63Befa7F7A825A3F8f |
Crafting | 0x61e8FaF08f25C0154F4196D5AaC99e56fAB5Aed5 |
CraftingSkills | 0xd181F8bCb765A4CF7390b87d296d2955F58d101B |
Feats | 0xED9750904Fee2731a0ad78a084781B56865269Bf |
Monster | 0xca050C0E5bC83Df0e1D0A5De14d0cEb44B0Dd7C9 |
Proficiency | 0x0b009e55Cbed833E0b29FCdfb0AD85765C1177C1 |
Random | 0x471a666dfcE27cbfa0895694858C3375828Dc35A |
Rarity | 0x8628684cc1DF8E986f2A3650eb77700b3995Ff79 |
Roll | 0xbb20DEFd74e43667c2637fa39970f932c0d974f7 |
Skills | 0xA749EF13D370F749c49295cD7E97f3aB5F2B024D |
Summoner | 0xb21f4E0B4E3f6F348eBE662489cE860dd8Fd3986 |
Name | Address |
---|---|
base_random_2 | 0x165AD01B090BC91352AeA8cEF7513C63852797Ed |
crafting_skills | 0xa0B2508A25dc28D20C537b8E1798543AC437F669 |
items_armor_2 | 0xe2e659caC782EC2A1FF041A79185AAaAcdA336f2 |
items_armor_masterwork | 0x763C2f6B31d0C695F7A6308a50E3f3107e65260c |
items_tools | 0xC3F59C8b7041285000F21D74485f6c70b21B5E92 |
items_tools_masterwork | 0x291D890a0410Ac98512569330C2Ad4861dC6C822 |
items_weapons_2 | 0x48F177ED0B38efab35D6150659eDAeCEE234E802 |
items_weapons_masterwork | 0x8834c3C74026468AE5d151bb77c2097E0184377e |
Masterwork items are exceptional. They are made so well that you get a bonus when using them.
β Masterwork weapons make you more accurate, granting a +1 bonus on all attacks. That is, you get a 10% better chance of hitting an Armor Class of 10, 5% better odds against AC 20, and so on.
π‘ Masterwork armor fits you perfectly, granting a +1 armor check bonus. This gives you better odds whenever your movement is in check, such as sneaking up on an opponent or climbing out of a trap.
π Masterwork tools are just what you needed to get the job done. They grant various skill bonuses such as crafting and lock picking.
π§ββοΈ Masterwork crafting is the basis for magic weapons and armor. Magic weapons and armor grant even more bonuses such as extra damage and improved armor class. Magic Crafting, coming soon..
π·ββοΈ The Rarity Core Library was created to support Masterwork. The core library contains everything you need to create your own on-chain d20 adventures in Solidity.
πΉ Nice!!
This repo contains all source code and tooling used to build and test Masterwork. Use it as a reference or template for integrating masterwork and other d20 mechanics with your game.
- Get started
- Rarity Crafting 2 - Masterwork Weapons, Armor, and Tools
- Rarity Adventure 2 - Monsters in the Barn
- Rarity Core Library
- How to use masterwork items in your game
- Testing
- Package commands
- Hardhat customizations
- Thank You πΉπ
git clone [email protected]:murderteeth/rarity-masterwork.git
cd rarity-masterwork
# config local .env file
yarn
yarn hardhat compile
yarn test
Masterwork items, like common items, are minted to your wallet as standard ERC721 tokens. Create masterwork items like this:
- Start a masterwork project
- Make craft checks until the project is complete
- Claim the masterwork item
- Starting a new masterwork project requires a payment in gold for raw materials. Approve the contract's apprentice to receive the fees like this:
const cost = await masterwork.projects.raw_materials_cost(
baseType.weapon,
weaponType.longsword
);
await gold.approve(summoner, await masterwork.projects.APPRENTICE(), cost);
Masterwork crafting also requires artisan's tools. You can use the masterwork crafting station to craft masterwork artisan's tools for a +2 bonus on craft checks. Until then, pay an extra 5gp to "rent" a set of common artisan tools:
let cost = await masterwork.projects.raw_materials_cost(
baseType.weapon,
weaponType.longsword
);
cost = cost.add(await masterwork.projects.COMMON_ARTISANS_TOOLS_RENTAL());
await gold.approve(summoner, await masterwork.projects.APPRENTICE(), cost);
- Next, start a project:
await masterwork.projects.start(summoner, baseType.weapon, weaponType.longsword, 0);
Or if you have masterwork artisan's tools, like this:
// first, approve masterwork to manage your artisan's tools
await masterwork.items.approve(masterwork.projects.address, toolsToken)
await masterwork.projects.start(summoner, baseType.weapon, weaponType.longsword, toolsToken)
Calling start
does this
- Transfer appropriate project costs, in gold, from
summoner
tomasterwork.APPRENTICE()
- Transfer your masterwork artisan's tools to the contract (optional)
- Mints a new masterwork ERC721 token to your wallet representing the project
Get project status like this:
const project = await masterwork.projects.projects(projectToken);
At anytime you can cancel a project and reclaim your masterwork artisan's tools:
await masterwork.projects.cancel(projectToken);
- Start crafting:
// first, approve masterwork to spend your summoner's xp
await rarity.approve(masterwork.projects.address, summoner)
await masterwork.projects.craft(projectToken, 0)
To speed up crafting specify bonus materials. You get +1 to your craft score for every 20 mats:
// for a +4 bonus
await masterwork.projects.craft(projectToken, ethers.utils.parseEther('80'))
Calling craft
does this
- Compute a craft check for
summoner
- roll 1 d20
- add
summoner
intelligence modifier - add
summoner
specialty crafting ranks appropriate for weapons or armor - or if crafting masterwork tools, add
summoner
base crafting skill ranks (no specialty required)
- If the score is equal or higher the item's DC (difficulty class), the check succeeds and the score is added to the project's total progress in exchange for
summoner
's experience points. If your score is high enough to complete the project,summoner
pays a prorated amount of XP. The XP cost of making a craft check is otherwise one day's work, or 250 XP. - If the craft check score is less than the item's DC, the check fails, no progress is made, and one day's XP is spent
- Burn bonus mats
Check the progress of a project:
const [progress, masterworkItemCostInSilver] = await masterwork.projects.get_progress(
projectToken
);
const percentDone = progress.div(masterworkItemCostInSilver);
Estimate a project's remaining XP cost:
const estimate = await masterwork.projects.estimate_remaining_xp_cost(
projectToken,
bonusMats
);
console.log("estimate", ethers.utils.formatEther(estimate));
Get a summoner's odds of succeeding the next craft check:
const [average_score, dc] = await masterwork.projects.get_craft_check_odds(
projectToken,
bonusMats
);
const odds = average_score / dc;
- When crafting is complete,
project.complete
, claim your masterwork item:
await masterwork.items.claim(projectToken)
- Reclaim your artisan's tools:
await masterwork.projects.reclaim_tools(projectToken)
Each craft attempt emits a Craft
event containing the craft check result, spent mats, spent xp, and crafting progress.
Masterwork weapons and armor require summoner's to take up specialized crafting skills. Specialization ranks are redeemable 1:1 for core skill ranks in crafting. The following specializations are available:
- Alchemy (for future expansion, spellcasters only)
- Armorsmithing
- Bowmaking
- Trapmaking (for future expansion)
- Weaponsmithing
Specialized skills are managed with the rarity_crafting_skills
contract which works like the existing core skills contract. For example, raise a summoner's weaponsmithing specialization like this:
const craftingSkills = await craftingSkills.get_skills(summoner);
craftingSkills[4] += 1;
await craftingSkills.set_skills(summoner, craftingSkills);
You can craft masterwork versions of all the items found in the core weapons and armor codexes.
You can also craft the following masterwork tools, found in the masterwork tools codex:
Masterwork Artisan's Tools - These tools serve the same purpose as artisan's tools, but masterwork artisan's tools are the perfect tools for the job, so you get a +2 circumstance bonus on Craft checks made with them.
Masterwork Musical Instrument - A masterwork instrument grants a +2 circumstance bonus on Perform checks involving its use.
Masterwork Thieves Tools - This kit contains extra tools and tools of better make, which grant a +2 circumstance bonus on Disable Device and Open Lock checks.
Masterwork Multitool - This well-made item is the perfect tool for the job. It grants a +2 circumstance bonus on a related skill check (if any). Bonuses provided by multiple masterwork items used toward the same skill check do not stack.
A codex a common tools, codex-items-tools.sol, is also available for future expansion.
The initial difficulty of a masterwork project is just the DC of the common version of the item being crafted. This is called the "standard component". Once enough progress has been made at the standard component DC the difficulty increases to the masterwork component DC (which is always 20). When the difficulty goes up so does the amount of progress you make on each check.
Most projects will require more than one craft check. As you make craft checks each score is aggregated into a total with this formula:
- St = total score
- Cs = the cost of a common version of the item being crafted, priced in silver
- score = current craft score
- DCs = the difficulty class of the item being crafted (aka, the standard component)
- DCm = the difficulty class of the item's masterwork component (always 20)
Progress is then computed as the ratio of your total score to the cost of the masterwork item priced in silver:
Thus, a bonus to your craft skill doesn't just give you better odds on passing a craft check. A bonus also "speeds up" your project by adding more to your progress on each succesful roll.
Masterwork adapts its crafting mechanics from the d20 rules below while also continuing ideas from the core common crafting contract. The mechanics have been set such that a level 6 crafter with maxed craft skills, and without supplying any bonus crafting mats, can complete a masterwork longsword for about 5 days of XP (one work week).
All crafts require artisan's tools to give the best chance of success. If improvised tools are used, the check is made with a -2 circumstance penalty. On the other hand, masterwork artisan's tools provide a +2 circumstance bonus on the check.
To determine how much time and money it takes to make an item, follow these steps.
- Find the item's price. Put the price in silver pieces (1 gp = 10 sp).
- Find the DC from the table below.
- Pay one-third of the item's price for the cost of raw materials.
- Make an appropriate Craft check representing one week's work. If the check succeeds, multiply your check result by the DC. If the result Γ the DC equals the price of the item in sp, then you have completed the item. (If the result Γ the DC equals double or triple the price of the item in silver pieces, then you've completed the task in one-half or one-third of the time. Other multiples of the DC reduce the time in the same manner.) If the result Γ the DC doesn't equal the price, then it represents the progress you've made this week. Record the result and make a new Craft check for the next week. Each week, you make more progress until your total reaches the price of the item in silver pieces.
from d20, under Creating Masterwork Items
You can make a masterwork itemβa weapon, suit of armor, shield, or tool that conveys a bonus on its use through its exceptional craftsmanship, not through being magical. To create a masterwork item, you create the masterwork component as if it were a separate item in addition to the standard item. The masterwork component has its own price (300 gp for a weapon or 150 gp for a suit of armor or a shield) and a Craft DC of 20. Once both the standard component and the masterwork component are completed, the masterwork item is finished.
Monsters in the Barn is a single player, turn-based combat encounter. The adventure begins outside a barn where monsters have been hording salvage. Choose a summoner, equip weapons and armor, enter the barn.. If you defeat the monsters, claim their salvage and use it to speed up crafting at the masterwork crafting station. If you loose, try again tomorrow. This adventure is minted to your wallet as a standard ERC721 token.
Monsters in the Barn is designed to be challenging for summoners level 1 through 9. Entering the barn initiates combat with up to 3 monsters. Summoners are matched against monsters having a CR (challenge rating) equal to their level or lower.
- These monsters will be tough. Send strong summoners, equip them well. Masterwork includes the
rarity_equipment_2
contract for equipping both common and masterwork weapons and armor. Equipment2 was adapted from Rarity Extended's equipment system, but only supports one weapon, armor, and a shield. Use it like this:
// first, approve the equipment contract to transfer your items
await masterwork.approve(equipment2.address, masterworkLongswordId)
await masterwork.approve(equipment2.address, masterworkFullPlateArmorId)
await masterwork.approve(equipment2.address, masterworkShieldId)
// equip each item, 1 = weapon, 2 = armor, 3 = shield
await equipment2.equip(summoner, 1, masterwork.address, masterworkLongswordId)
await equipment2.equip(summoner, 2, masterwork.address, masterworkFullPlateArmorId)
await equipment2.equip(summoner, 3, masterwork.address, masterworkShieldId)
Equipping an item transfers it into the equipment contract. When the item is unequipped, the item is returned to your wallet:
await equipment2.unequip(summoner, 1)
await equipment2.unequip(summoner, 2)
await equipment2.unequip(summoner, 3)
Players may also choose to fight unarmed and/or unarmored. This is only recommended for Monks, however, who receive attack and armor bonuses per d20.
- Start a new Monsters in the Barn adventure by calling
start
:
// first, approve adventure to manage your summoner
await rarity.approve(barnAdventure.address, summoner)
await barnAdventure.start(summoner)
Calling start
does this
- Transfer
summoner
to the adventure contract - Mints a new ERC721 token to your wallet representing the adventure
Get adventure status like this:
const adventure = await barnAdventure.adventures(adventureToken)
At anytime you can end the adventure and reclaim your summoner:
await barnAdventure.end(adventureToken)
- Enter the barn..
await barnAdventure.enter_dungeon(adventureToken)
Calling enter_dungeon
does this
- Randomly "mints" up to 3 monsters
- Rolls initiative for the summoner and each monster
- Orders the combatants by their initiative scores into a Turn Order
- Starting at the top of the Turn Order, combatants take their turns until it's the summoner's turn
Enumerate the turn order like this:
const combatants[]
const combatantCount = await barnAdventure.adventures(token).monster_count + 1
for(let i = 0; i < combatantCount; i++) {
combatants.push(await barnAdventure.turn_orders(adventureToken, i))
}
Get the summoner's turn order index like this:
const summonersTurn = await barnAdventure.summoners_turns(adventureToken)
- Attack! When it's your summoner's turn you can
attack
orflee
. To attack, chose a target by their turn order index. For convenience, you can "auto target" monsters using a call tonext_able_monster
.
const target = await barnAdventure.next_able_monster(adventureToken)
await barnAdventure.attack(adventureToken, target)
Calling attack
does this
- Roll attack for the adventure's
summoner
- If the attack score is equal or higher the target monster's AC (armor class), the attack hits, and a damage roll is made
- If the attack roll is a natural 20 (or within the equipped weapon's critical range), the attack is critical, and extra damage is rolled according to the weapon's critical multiplier
- If the attack score is less than the monster's AC, the attack is a miss
- If the target's hit points (HP) are brought below zero, the monster is dying and considered slain
- If the summoner has no more attacks for the round, monsters take their turns until it's the summoner's next turn
Some monsters get more than one attack per round. The good news. Barbarians, fighters, paladins, and rangers also get extra attacks per round starting at level 6! The adventure contract keeps track of these attacks for you. To get the current attack number:
const attackCounter = await barnAdventure.attack_counters(adventureToken)
But generally, while combat is ongoing, it will always be the summoner's turn from the perspective of a contract client (as the monsters' moves are played automatically between summoner moves). So you can just call attack
until combat is over:
await barnAdventure.is_combat_over(adventureToken)
- Ending combat - Combat ends automatically when either the summoner or all the monsters are below 0 hit points. Alternatively, you can also chose to flee:
await barnAdventure.flee(adventureToken)
Fleeing doesn't do anything special beside set the combat to over. But it's provided for narrative flavor and is reflected in the adventure token's URI. You can also simply end
the entire adventure whenever you like.
- Victory !! To win the dungeon your summoner must defeat all the monsters. If you are victorious, run an optional search check for a loot bonus on the monsters' salvage:
await barnAdventure.search(adventureToken)
The search check goes like this
- Roll 1 d20
- Add
summoner
search skill ranks - +2 if the
summoner
has the investigator feat
If the score is greater or equal the adventure's search DC (20), you get a 15% bonus. If you roll a natural 20, you get a 20% bonus. Nice! Now you can end the adventure and claim your loot:
await barnAdventure.end(adventureToken)
await crafingMaterials2.claim(adventureToken)
Claim barn salvage mats for victory in the barn. These mats are redeemable at 10:1 against each monster's CR. That is, slaying a monster with CR 4 awards 40 mats. These mats are minted to your wallet as standard ERC20 tokens.
Each attack emits an Attack
event containing attacker, defender, and attack results.
The mechanics of Monsters in the Barn follow d20 combat closely, but only cover the very basics. Future expansions will cover more advanced mechanics like movement, ranged weapons, spells, saving throws, conditions, and buffs. For more, check out d20 Combat.
- Kobold (CR 1/4)
- Goblin (CR 1/3)
- Gnoll (CR 1)
- Black Bear (CR 2)
- Ogre (CR 3)
- Dire Boar (CR 4)
- Dire Wolverine (CR 4)
- Troll (CR 5)
- Ettin (CR 6)
An ad hoc monster codex is available in the library.
Masterwork's crafting and dungeon mechanics are complex. For sanity's sake we started a rarity core solidity library to abstract everything a builder needs to create their own d20 adventures.
Consider the library's combat system. The combat system lets any character attack any other character using d20 rules to compute the outcome. It does this by requiring that each fighter be adapted to a standard Combatant
struct. This allows the combat system to run d20 combat rules against a common interface and enables summoner vs monster and summoner vs summoner combat.. it also enables monster vs monster and, in theory, any nft vs any nft.
Check out the Combatant
stuct:
struct Combatant {
uint8 initiative_roll;
int8 initiative_score;
uint8 armor_class;
int16 hit_points;
address origin;
uint256 token;
int8[28] attacks;
}
- initiative_roll/score - Determines turn order
- armor_class - How difficult it is to hit this combatant
- hit_points - How much damage can be taken
- origin - Contract address that issues this combatant's underlying token
- token - This combatant's underlying nft (eg, a Summoner Id)
- attacks - An array containing all the combatant's attacks per round
The current attacks
array can hold up to 4 attacks. Each attack has these properties:
- attack_bonus
- critical_modifier
- critical_multiplier
- damage_dice_count
- damage_dice_sides
- damage_modifier
- damage_type
Helper functions for packing and unpacking the attacks
array live in the Combat library.
Monsters in the Barn implements summoner vs monster combat. To adapt summoners and monsters to the Combatant
struct it uses these two functions:
function summoner_combatant(
uint256 summoner,
Equipment.Slot[3] memory loadout
) public view returns (Combat.Combatant memory combatant) {
(uint8 initiative_roll, int8 initiative_score) = Roll.initiative(
summoner
);
Equipment.Slot memory weapon_slot = loadout[0];
Equipment.Slot memory armor_slot = loadout[1];
Equipment.Slot memory shield_slot = loadout[2];
combatant.mint = address(0xce761D788DF608BD21bdd59d6f4B54b2e27F25Bb);
combatant.token = summoner;
combatant.initiative_roll = initiative_roll;
combatant.initiative_score = initiative_score;
combatant.hit_points = int16(uint16(hit_points(summoner)));
combatant.armor_class = armor_class(summoner, armor_slot, shield_slot);
combatant.attacks = attacks(
summoner,
weapon_slot,
armor_slot,
shield_slot
);
}
function monster_combatant(
uint256 token,
address mint,
Monster.MonsterCodex memory monster_codex
) public view returns (Combat.Combatant memory combatant) {
(uint8 initiative_roll, int8 initiative_score) = Roll.initiative(
token,
Attributes.compute_modifier(monster_codex.abilities[1]),
monster_codex.initiative_bonus
);
combatant.mint = mint;
combatant.token = token;
combatant.initiative_roll = initiative_roll;
combatant.initiative_score = initiative_score;
combatant.hit_points = standard_hit_points(monster_codex);
combatant.armor_class = monster_codex.armor_class;
combatant.attacks = monster_codex.attacks;
}
Note the use of the Equipment.Slot
stuct and several functions from the Roll
, Summoner
, and Monster
libraries to make adapting the Combatant struct easy. With those adapters in place Monsters in the Barn can run an attack like this:
(bool hit, uint8 roll, int8 score, uint8 critical_confirmation, uint8 damage, uint8 damage_type)
= Combat.attack_combatant(attacker, defender, attack_number);
The key feature of a masterwork longsword is the +1 attack bonus it grants its wielder. Masterwork introduces a series of extensions to the original Rarity codexes that include these bonuses. In the simplest case, you can query an item's codex directly for a bonus like this:
int8 longsword_attack_bonus = masterwork_weapons_codex.get_attack_bonus(longswordToken);
Another option is to let the core library do it for you by using the Equipment.Slot
and Combatant
structs. This is how Monsters in the Barn is implemented.
For example, consider the Summoner library's preview
function:
function preview(
uint256 summoner,
address weapon_mint,
uint256 weapon_token,
address armor_mint,
uint256 armor_token,
address shield_mint,
uint256 shield_token
) public view returns (Combat.Combatant memory result) {
Equipment.Slot memory weapon_slot = Equipment.Slot(
weapon_mint,
weapon_token
);
Equipment.Slot memory armor_slot = Equipment.Slot(
armor_mint,
armor_token
);
Equipment.Slot memory shield_slot = Equipment.Slot(
shield_mint,
shield_token
);
result.token = summoner;
result.mint = address(Rarity.RARITY);
result.hit_points = int16(uint16(hit_points(summoner)));
result.armor_class = armor_class(summoner, armor_slot, shield_slot);
result.attacks = attacks(
summoner,
weapon_slot,
armor_slot,
shield_slot
);
}
The preview function can be used by a client to see the effects of equipping an item before actually equipping it. Clients can call preview like this:
const preview = await summonerLibrary.preview(
fighter,
longsword,
crafting.masterwork.address,
fullplate,
crafting.common.address,
0,
ethers.constants.AddressZero
);
const fullPrimaryAttackBonus = unpackAttacks(preview.attacks)[0].attack_bonus;
unpackAttacks
is a utility provided here.
Masterwork extends the original weapons and armor codexes to expose bonus effects. Several missing item descriptions were also filled in. And the "axe" in the original codex has been upgraded to the throwing axe described in d20. (That seemed like the original intention)
There's a few unit tests. Run them like this
yarn test
The acceptance test is designed to run against a local hardhat network. The Easiest way to run it, start a console and run this
yarn start-fork
Then open another console and run these
yarn hardhat run scripts/deploy.ts --network localhost
yarn hardhat run scripts/acceptance-test/--1-train-your-party.ts --network localhost
yarn hardhat run scripts/acceptance-test/--2-craft-common-equipment.ts --network localhost
yarn hardhat run scripts/acceptance-test/--3-raid-the-barn.ts --network localhost
yarn hardhat run scripts/acceptance-test/--4-craft-masterwork-equipment.ts --network localhost
yarn hardhat run scripts/acceptance-test/--5-raid-the-barn-again.ts --network localhost
yarn test
yarn test-fast
yarn report-gas
yarn random-uint256 # handy for generating random seeds
yarn size-contracts # get compiled size of all the contracts
This project uses hardhat for its solidity dev environment. The following customizations have been made.
This project also uses typechain to generate typescript types for all the core contracts and libraries. Unfortunately the current typechain has a known name-colision problem when generating types across nested directories. A future release of typechain promises to fix this. For now, this project overrides hardhat's TASK_COMPILE_SOLIDITY_COMPILE_JOBS
compile task and generates the typechain types manually as a workaround.
This project also includes a custom hardhat task that generates full interfaces on all contracts. The results are saved here. This can be run manually with:
yarn hardhat rarity-interfaces
Please join me and say thanks to these great folks:
Hrunting is a table-top DM guru and has been advising on how to adapt d20 to solidity. Hrunting gave critical input on the design of Monsters in the Barn and was first to point out that Rarity needs a masterwork crafting level.
zgohr, creator of Rarity Homestead
Homestead wrote the first draft of the masterwork dungeon and core library. This was a challenging task and Homestead delivered, contributing many insights in addition to code.
Patrician, of CryptoShuraba and RarityFoundation
Patrician reviewed masterwork, challenged all the questionable choices, and provided actionable insights from a DM's perspective. He's also been great at raising awareness of both masterwork and Rarity as a whole.
Masterwork borrows some great ideas from Extended's Rarity Extended Lib. The Extended team also reviewed masterwork and has been providing invaluable guidance since Rarity started last year.
The Shuraba team also gave a review to masterwork. In addtion, they generously granted the project 1300 MST, no strings.