diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f6f8e083..9a7ceca5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,16 @@ +## v.1.4.7 + +### Bugfixes +* [#530] Token art mapping from Pathfinder Token Pack: Bestiaries module not working +* [#531] Missing attack bonuses on the following Talents + - +1 to Melee Attacks and Damage + - +1 to Ranged Attacks and Damage + +### Enhancements +* [#527] New predefined effect that will add selected attribute bonuses to AC (if positive) +* [#528] Add built-in effect to support weapon damage dice improvent via matching weapon properties +* [#529] Add predefined affect that adds to AC if no armor is worn + ## v.1.4.6 ### Bugfixes diff --git a/data/packs/talents.db/_1_to_melee_attacks_and_damage__0yk16c9qcJ9vCekD.json b/data/packs/talents.db/_1_to_melee_attacks_and_damage__0yk16c9qcJ9vCekD.json index e3c30d5d..f004e8de 100644 --- a/data/packs/talents.db/_1_to_melee_attacks_and_damage__0yk16c9qcJ9vCekD.json +++ b/data/packs/talents.db/_1_to_melee_attacks_and_damage__0yk16c9qcJ9vCekD.json @@ -2,7 +2,8 @@ "_id": "0yk16c9qcJ9vCekD", "_key": "!items!0yk16c9qcJ9vCekD", "effects": [ - "JOP97BP4aJ0kjcgL" + "JOP97BP4aJ0kjcgL", + "c7CGt0FBJHv5BtpM" ], "folder": "Z4zDnZoxthjHXo5V", "img": "icons/skills/melee/blade-tip-chipped-blood-red.webp", diff --git a/data/packs/talents.db/_1_to_ranged_attacks_and_damage__2IfYKGhWxyd3Ldr5.json b/data/packs/talents.db/_1_to_ranged_attacks_and_damage__2IfYKGhWxyd3Ldr5.json index bc9b6bd0..c198a00f 100644 --- a/data/packs/talents.db/_1_to_ranged_attacks_and_damage__2IfYKGhWxyd3Ldr5.json +++ b/data/packs/talents.db/_1_to_ranged_attacks_and_damage__2IfYKGhWxyd3Ldr5.json @@ -2,7 +2,8 @@ "_id": "2IfYKGhWxyd3Ldr5", "_key": "!items!2IfYKGhWxyd3Ldr5", "effects": [ - "lodEOfzuI5jDWc4h" + "lodEOfzuI5jDWc4h", + "OQQ5fxWh3zBw8W5O" ], "folder": "Z4zDnZoxthjHXo5V", "img": "icons/weapons/ammunition/arrow-head-war-flight.webp", diff --git a/data/packs/talents.db/melee_attack_roll_bonus__c7CGt0FBJHv5BtpM.json b/data/packs/talents.db/melee_attack_roll_bonus__c7CGt0FBJHv5BtpM.json new file mode 100644 index 00000000..e121db00 --- /dev/null +++ b/data/packs/talents.db/melee_attack_roll_bonus__c7CGt0FBJHv5BtpM.json @@ -0,0 +1,29 @@ +{ + "_id": "c7CGt0FBJHv5BtpM", + "_key": "!items.effects!0yk16c9qcJ9vCekD.c7CGt0FBJHv5BtpM", + "changes": [ + { + "key": "system.bonuses.meleeAttackBonus", + "mode": 2, + "priority": null, + "value": "1" + } + ], + "description": "", + "disabled": false, + "duration": { + "combat": null, + "rounds": null, + "seconds": null, + "startRound": null, + "startTime": null, + "startTurn": null, + "turns": null + }, + "icon": "icons/skills/melee/strike-polearm-glowing-white.webp", + "name": "Melee Attack Roll Bonus", + "origin": null, + "statuses": [ + ], + "transfer": true +} diff --git a/data/packs/talents.db/ranged_attack_roll_bonus__OQQ5fxWh3zBw8W5O.json b/data/packs/talents.db/ranged_attack_roll_bonus__OQQ5fxWh3zBw8W5O.json new file mode 100644 index 00000000..f9a4cd9c --- /dev/null +++ b/data/packs/talents.db/ranged_attack_roll_bonus__OQQ5fxWh3zBw8W5O.json @@ -0,0 +1,29 @@ +{ + "_id": "OQQ5fxWh3zBw8W5O", + "_key": "!items.effects!2IfYKGhWxyd3Ldr5.OQQ5fxWh3zBw8W5O", + "changes": [ + { + "key": "system.bonuses.rangedAttackBonus", + "mode": 2, + "priority": null, + "value": "1" + } + ], + "description": "", + "disabled": false, + "duration": { + "combat": null, + "rounds": null, + "seconds": null, + "startRound": null, + "startTime": null, + "startTurn": null, + "turns": null + }, + "icon": "icons/weapons/ammunition/arrow-head-war-flight.webp", + "name": "Ranged Attack Roll Bonus", + "origin": null, + "statuses": [ + ], + "transfer": true +} diff --git a/i18n/en.yaml b/i18n/en.yaml index a94947db..226623f4 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -70,6 +70,10 @@ SHADOWDARK.armor.properties.disadvantage_swimming: Disadvantage/Swim SHADOWDARK.armor.properties.no_swimming: No Swim SHADOWDARK.armor.properties.one_handed: Occupies One Hand SHADOWDARK.armor.properties.shield: Shield +SHADOWDARK.boon.type.label: Boon Type +SHADOWDARK.boons.blessing: Blessing +SHADOWDARK.boons.oath: Oath +SHADOWDARK.boons.secret: Secret SHADOWDARK.chat_card.button.attack: Roll Attack SHADOWDARK.chat_card.button.cast_spell: Cast Spell SHADOWDARK.chat_card.button.learn_spell: Learn Spell @@ -139,9 +143,9 @@ SHADOWDARK.class.thief: Thief SHADOWDARK.class.titles.levels.from.label: From SHADOWDARK.class.titles.levels.label: Levels SHADOWDARK.class.titles.levels.to.label: To -SHADOWDARK.class.weapons.all.label: All Weapons SHADOWDARK.class.weapons.all_melee.label: All Melee Weapons SHADOWDARK.class.weapons.all_ranged.label: All Ranged Weapons +SHADOWDARK.class.weapons.all.label: All Weapons SHADOWDARK.class.weapons.label: Weapons SHADOWDARK.class.weapons.prompt: Select Weapon... SHADOWDARK.class.wizard: Wizard @@ -158,8 +162,10 @@ SHADOWDARK.dialog.ability_check.str: Strength Check SHADOWDARK.dialog.ability_check.title: Ability Check SHADOWDARK.dialog.ability_check.wis: Wisdom Check SHADOWDARK.dialog.effect.choice.armor: Choose Armor Type +SHADOWDARK.dialog.effect.choice.attribute: Choose Attribute SHADOWDARK.dialog.effect.choice.lightSource: Choose Light Source SHADOWDARK.dialog.effect.choice.spell: Choose Spell +SHADOWDARK.dialog.effect.choice.weapon_property: Choose Weapon Property SHADOWDARK.dialog.effect.choice.weapon: Choose Weapon Type SHADOWDARK.dialog.general.are_you_sure: Are you sure? SHADOWDARK.dialog.general.cancel: Cancel @@ -297,6 +303,7 @@ SHADOWDARK.item.effect.predefined_effect.abilityImprovementInt: Ability Score Im SHADOWDARK.item.effect.predefined_effect.abilityImprovementStr: Ability Score Improvement (Str) SHADOWDARK.item.effect.predefined_effect.abilityImprovementWis: Ability Score Improvement (Wis) SHADOWDARK.item.effect.predefined_effect.acBonus: AC Bonus +SHADOWDARK.item.effect.predefined_effect.acBonusFromAttribute: AC Bonus from Attribute SHADOWDARK.item.effect.predefined_effect.additionalGearSlots: Additional Gear Slots SHADOWDARK.item.effect.predefined_effect.armorMastery: Armor Mastery SHADOWDARK.item.effect.predefined_effect.backstabDie: Additional Backstab Die @@ -319,9 +326,11 @@ SHADOWDARK.item.effect.predefined_effect.rangedAttackBonus: Ranged Attack Roll B SHADOWDARK.item.effect.predefined_effect.rangedDamageBonus: Ranged Damage Bonus SHADOWDARK.item.effect.predefined_effect.spellAdvantage: Spellcasting Advantage on Spell SHADOWDARK.item.effect.predefined_effect.spellCastingBonus: Spellcasting Check Bonus +SHADOWDARK.item.effect.predefined_effect.unarmoredAcBonus: Unarmored AC Bonus SHADOWDARK.item.effect.predefined_effect.weaponAttackBonus: Weapon Attack Roll Bonus SHADOWDARK.item.effect.predefined_effect.weaponDamageBonus: Weapon Attack Damage Bonus SHADOWDARK.item.effect.predefined_effect.weaponDamageDieD12: Weapon Damage Die D12 +SHADOWDARK.item.effect.predefined_effect.weaponDamageDieImprovementByProperty: Weapon Damage Die Improvement By Property SHADOWDARK.item.effect.predefined_effect.weaponDamageMultiplier: Weapon Damage Multiplier SHADOWDARK.item.effect.predefined_effect.weaponMastery: Weapon Mastery SHADOWDARK.item.effect.show-on-panel: Show on panel @@ -531,6 +540,10 @@ SHADOWDARK.sheet.player.ancestry.tooltip: Your character's cultural and ancestra SHADOWDARK.sheet.player.available_spells: Available Spells SHADOWDARK.sheet.player.background.label: Background SHADOWDARK.sheet.player.background.tooltip: Your history and past experience. You are adept at tasks related to your background. +SHADOWDARK.sheet.player.boons.blessings.label: Blessings +SHADOWDARK.sheet.player.boons.label: Boons +SHADOWDARK.sheet.player.boons.oaths.label: Oaths +SHADOWDARK.sheet.player.boons.secrets.label: Secrets SHADOWDARK.sheet.player.class.label: Class SHADOWDARK.sheet.player.class.tooltip: Your character's job SHADOWDARK.sheet.player.deity.label: Deity @@ -602,11 +615,3 @@ SHADOWDARK.weapon.properties.two_handed: Two-Handed SHADOWDARK.weapon.properties.versatile: Versatile SHADOWDARK.weapon.type.melee: Melee SHADOWDARK.weapon.type.ranged: Ranged -SHADOWDARK.sheet.player.boons.label: Boons -SHADOWDARK.sheet.player.boons.oaths.label: Oaths -SHADOWDARK.sheet.player.boons.secrets.label: Secrets -SHADOWDARK.sheet.player.boons.blessings.label: Blessings -SHADOWDARK.boons.oath: Oath -SHADOWDARK.boons.secret: Secret -SHADOWDARK.boons.blessing: Blessing -SHADOWDARK.boon.type.label: Boon Type diff --git a/system/assets/mappings/map-predefined-effects.json b/system/assets/mappings/map-predefined-effects.json index 3a0e9140..cdb36cbe 100644 --- a/system/assets/mappings/map-predefined-effects.json +++ b/system/assets/mappings/map-predefined-effects.json @@ -48,6 +48,13 @@ "lang": "SHADOWDARK.item.effect.predefined_effect.acBonus", "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" }, + "acBonusFromAttribute": { + "defaultValue": "REPLACEME", + "effectKey": "system.bonuses.acBonusFromAttribute", + "icon": "icons/skills/melee/shield-block-gray-orange.webp", + "lang": "SHADOWDARK.item.effect.predefined_effect.acBonusFromAttribute", + "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" + }, "additionalGearSlots": { "defaultValue": 1, "effectKey": "system.bonuses.gearSlots", @@ -203,6 +210,13 @@ "lang": "SHADOWDARK.item.effect.predefined_effect.spellCastingBonus", "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" }, + "unarmoredAcBonus": { + "defaultValue": 1, + "effectKey": "system.bonuses.unarmoredAcBonus", + "icon": "icons/skills/melee/shield-block-gray-orange.webp", + "lang": "SHADOWDARK.item.effect.predefined_effect.unarmoredAcBonus", + "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" + }, "weaponAttackBonus": { "defaultValue": 1, "effectKey": "system.bonuses.attackBonus", @@ -228,6 +242,13 @@ "lang": "SHADOWDARK.item.effect.predefined_effect.weaponDamageDieD12", "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" }, + "weaponDamageDieImprovementByProperty": { + "defaultValue": "REPLACEME", + "effectKey": "system.bonuses.weaponDamageDieImprovementByProperty", + "icon": "icons/skills/ranged/arrows-flying-salvo-blue-light.webp", + "lang": "SHADOWDARK.item.effect.predefined_effect.weaponDamageDieImprovementByProperty", + "mode": "CONST.ACTIVE_EFFECT_MODES.ADD" + }, "weaponDamageMultiplier": { "defaultValue": 2, "effectKey": "system.bonuses.damageMultiplier", diff --git a/system/src/config.mjs b/system/src/config.mjs index 705f4ecb..7fdba7ef 100644 --- a/system/src/config.mjs +++ b/system/src/config.mjs @@ -38,6 +38,14 @@ SHADOWDARK.DICE = { d20: "d20", }; +SHADOWDARK.DAMAGE_DICE = [ + "d4", + "d6", + "d8", + "d10", + "d12", +]; + SHADOWDARK.BOON_TYPES = { oath: "SHADOWDARK.boons.oath", secret: "SHADOWDARK.boons.secret", diff --git a/system/src/dice/RollSD.mjs b/system/src/dice/RollSD.mjs index 8443fd71..b2e2fad9 100644 --- a/system/src/dice/RollSD.mjs +++ b/system/src/dice/RollSD.mjs @@ -308,6 +308,24 @@ export default class RollSD extends Roll { ? data.item.system.damage.twoHanded : false; + // Improve the base damage die if this weapon has the relevant property + const weaponDamageDieImprovementByProperty = + data.actor.system.bonuses.weaponDamageDieImprovementByProperty; + + for (const property of weaponDamageDieImprovementByProperty) { + if (await data.item.hasProperty(property)) { + damageDie = shadowdark.utils.getNextDieInList( + damageDie, + shadowdark.config.DAMAGE_DICE + ); + + versatileDamageDie = shadowdark.utils.getNextDieInList( + versatileDamageDie, + shadowdark.config.DAMAGE_DICE + ); + } + } + // Check if damage die is modified by talent if (data.actor.system.bonuses.weaponDamageDieD12.some( t => [data.item.name.slugify(), data.item.system.baseWeapon.slugify()].includes(t) diff --git a/system/src/documents/ActorSD.mjs b/system/src/documents/ActorSD.mjs index e3749b97..e1e8801b 100644 --- a/system/src/documents/ActorSD.mjs +++ b/system/src/documents/ActorSD.mjs @@ -168,6 +168,22 @@ export default class ActorSD extends Actor { // Find out if the user has a modified damage die let oneHanded = item.system.damage.oneHanded ?? false; let twoHanded = item.system.damage.twoHanded ?? false; + + // Improve the base damage die if this weapon has the relevant property + for (const property of this.system.bonuses.weaponDamageDieImprovementByProperty) { + if (await item.hasProperty(property)) { + oneHanded = shadowdark.utils.getNextDieInList( + oneHanded, + shadowdark.config.DAMAGE_DICE + ); + + twoHanded = shadowdark.utils.getNextDieInList( + twoHanded, + shadowdark.config.DAMAGE_DICE + ); + } + } + if (this.system.bonuses.weaponDamageDieD12.some(t => [item.name.slugify(), item.system.baseWeapon.slugify()].includes(t) )) { @@ -884,6 +900,11 @@ export default class ActorSD extends Actor { let baseArmorClass = shadowdark.defaults.BASE_ARMOR_CLASS; baseArmorClass += dexModifier; + for (const attribute of this.system.bonuses?.acBonusFromAttribute ?? []) { + const attributeBonus = this.abilityModifier(attribute); + baseArmorClass += attributeBonus > 0 ? attributeBonus : 0; + } + let newArmorClass = baseArmorClass; let armorMasteryBonus = 0; @@ -927,6 +948,9 @@ export default class ActorSD extends Actor { newArmorClass += armorMasteryBonus; } + else { + newArmorClass += this.system.bonuses.unarmoredAcBonus ?? 0; + } // Add AC from bonus effects newArmorClass += parseInt(this.system.bonuses.acBonus, 10); diff --git a/system/src/documents/ItemSD.mjs b/system/src/documents/ItemSD.mjs index 3d295a6c..f18dfacc 100644 --- a/system/src/documents/ItemSD.mjs +++ b/system/src/documents/ItemSD.mjs @@ -364,12 +364,10 @@ export default class ItemSD extends Item { * @returns {Object} */ async _handlePredefinedEffect(key, value) { - if (["weaponMastery", "weaponDamageDieD12"].includes(key)) { + if (key === "acBonusFromAttribute") { return this._askEffectInput( - "weapon", - await shadowdark.utils.getSlugifiedItemList( - await shadowdark.compendiums.baseWeapons() - ) + "attribute", + shadowdark.config.ABILITIES_LONG ); } else if (key === "armorMastery") { @@ -380,14 +378,6 @@ export default class ItemSD extends Item { ) ); } - else if (key === "spellAdvantage") { - return this._askEffectInput( - "spell", - await shadowdark.utils.getSlugifiedItemList( - await shadowdark.compendiums.spells() - ) - ); - } else if (key === "lightSource") { // TODO Need to move to light source objects to allow customisation const lightSourceList = await foundry.utils.fetchJsonWithTimeout( @@ -399,6 +389,31 @@ export default class ItemSD extends Item { }); return this._askEffectInput("lightSource", lightSources); } + else if (key === "spellAdvantage") { + return this._askEffectInput( + "spell", + await shadowdark.utils.getSlugifiedItemList( + await shadowdark.compendiums.spells() + ) + ); + } + else if (["weaponMastery", "weaponDamageDieD12"].includes(key)) { + return this._askEffectInput( + "weapon", + await shadowdark.utils.getSlugifiedItemList( + await shadowdark.compendiums.baseWeapons() + ) + ); + } + else if (key === "weaponDamageDieImprovementByProperty") { + return this._askEffectInput( + "property", + await shadowdark.utils.getSlugifiedItemList( + await shadowdark.compendiums.weaponProperties() + ) + ); + } + return [value]; } diff --git a/system/src/sheets/ItemSheetSD.mjs b/system/src/sheets/ItemSheetSD.mjs index 00961001..626601ca 100644 --- a/system/src/sheets/ItemSheetSD.mjs +++ b/system/src/sheets/ItemSheetSD.mjs @@ -144,8 +144,9 @@ export default class ItemSheetSD extends ItemSheet { context.classTalentTables = {}; for (const classTalentTable of classTalentTables) { + context.classTalentTables[classTalentTable.uuid] = - classTalentTable.name; + classTalentTable.name.replace(/^Class\s+Talents:\s/, ""); } const [selectedLanguages, availableLanguages] = diff --git a/system/src/utils/UtilitySD.mjs b/system/src/utils/UtilitySD.mjs index 5017f855..b3c862b5 100644 --- a/system/src/utils/UtilitySD.mjs +++ b/system/src/utils/UtilitySD.mjs @@ -27,6 +27,18 @@ export default class UtilitySD { return [selectedItems, unselectedItems]; } + static getNextDieInList(die, allDice) { + if (die === false) return die; + + for (let i = 0; i < allDice.length; i++) { + if (allDice[i] === die && allDice.length > i + 1) { + return allDice[i + 1]; + } + } + + return die; + } + static async getSlugifiedItemList(items) { const itemList = {}; items.map(i => itemList[i.name.slugify()] = i.name ); diff --git a/system/src/utils/module-art.mjs b/system/src/utils/module-art.mjs index 639f2953..4471853f 100644 --- a/system/src/utils/module-art.mjs +++ b/system/src/utils/module-art.mjs @@ -51,7 +51,11 @@ export class ModuleArt { settings ??= {portraits: true, tokens: true}; - for (const [packName, actors] of Object.entries(mapping)) { + for (let [packName, actors] of Object.entries(mapping)) { + packName = packName === "shadowdark.monster" + ? "shadowdark.monsters" + : packName; + const pack = game.packs.get(packName); if (!pack) continue; diff --git a/system/system.json b/system/system.json index 1a6231f3..430f079f 100644 --- a/system/system.json +++ b/system/system.json @@ -2,7 +2,7 @@ "id": "shadowdark", "title": "Shadowdark RPG", "desciption": "A system for playing the Shadowdark RPG from Arcane Library", - "version": "1.4.6", + "version": "1.4.7", "compatibility": { "minimum": "11", "verified": "11" @@ -12,7 +12,7 @@ }, "url": "https://github.com/Muttley/foundryvtt-shadowdark", "manifest": "https://raw.githubusercontent.com/Muttley/foundryvtt-shadowdark/master/system/system.json", - "download": "https://github.com/Muttley/foundryvtt-shadowdark/releases/download/release-1.4.6/shadowdark-release-1.4.6.zip", + "download": "https://github.com/Muttley/foundryvtt-shadowdark/releases/download/release-1.4.7/shadowdark-release-1.4.7.zip", "authors": [ { "name": "Paul Maskelyne", diff --git a/system/template.json b/system/template.json index e2cecadc..201aea74 100644 --- a/system/template.json +++ b/system/template.json @@ -95,6 +95,7 @@ "background": "", "bonuses": { "acBonus": 0, + "acBonusFromAttribute": [], "advantage": [], "armorMastery": [], "backstabDie": 0, @@ -112,7 +113,9 @@ "rangedAttackBonus": 0, "rangedDamageBonus": 0, "spellcastingCheckBonus": 0, + "unarmoredAcBonus": 0, "weaponDamageDieD12": [], + "weaponDamageDieImprovementByProperty": [], "weaponMastery": [] }, "class": "",