Skip to content

Commit

Permalink
[foundryvtt#1348] Add feature type to feat items
Browse files Browse the repository at this point in the history
  • Loading branch information
arbron committed Dec 29, 2022
1 parent 16c334a commit e14555e
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 2 deletions.
1 change: 1 addition & 0 deletions dnd5e.css
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,7 @@ h5 {
margin: 0;
padding: 5px;
text-align: right;
white-space: nowrap;
color: #7a7971;
}
.dnd5e.sheet.item .sheet-header .item-subtitle .item-type {
Expand Down
22 changes: 22 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,20 @@
"DND5E.ChatContextDoubleDamage": "Apply Double Damage",
"DND5E.ChatContextHalfDamage": "Apply Half Damage",
"DND5E.ChatFlavor": "Chat Message Flavor",
"DND5E.ClassFeature.ArtificerInfusion": "Artificer Infusion",
"DND5E.ClassFeature.ChannelDivinity": "Channel Divinity",
"DND5E.ClassFeature.DefensiveTactic": "Defensive Tactic",
"DND5E.ClassFeature.EldritchInvocation": "Eldritch Invocation",
"DND5E.ClassFeature.ElementalDiscipline": "Elemental Discipline",
"DND5E.ClassFeature.FightingStyle": "Fighting Style",
"DND5E.ClassFeature.HuntersPrey": "Hunter's Prey",
"DND5E.ClassFeature.Ki": "Ki Ability",
"DND5E.ClassFeature.Maneuver": "Maneuver",
"DND5E.ClassFeature.Metamagic": "Metamagic Option",
"DND5E.ClassFeature.Multiattack": "Multiattack",
"DND5E.ClassFeature.PsionicPower": "Psionic Power",
"DND5E.ClassFeature.Rune": "Rune",
"DND5E.ClassFeature.SuperiorHuntersDefense": "Superior Hunter's Defense",
"DND5E.ClassIdentifier": "Class Identifier",
"DND5E.ClassIdentifierHint": "This class's data will be accessible using <strong>@classes.{identifier}</strong> in roll formulas.",
"DND5E.ClassLevels": "Class Levels",
Expand Down Expand Up @@ -439,6 +453,11 @@
"DND5E.Exhaustion": "Exhaustion",
"DND5E.Expertise": "Expertise",
"DND5E.FeatureActionRecharge": "Action Recharge",
"DND5E.Feature.Background": "Background Feature",
"DND5E.Feature.Class": "Class Feature",
"DND5E.Feature.Feat": "Feat",
"DND5E.Feature.Monster": "Monster Feature",
"DND5E.Feature.Species": "Species Feature",
"DND5E.Flaws": "Flaws",
"DND5E.FormulaCannotContainDiceError": "{name} formula cannot contain dice expressions.",
"DND5E.EffectCreate": "Create Effect",
Expand Down Expand Up @@ -576,6 +595,9 @@
"DND5E.ItemEquipmentStealthDisav": "Imposes Stealth Disadvantage",
"DND5E.ItemEquipmentType": "Equipment Type",
"DND5E.ItemEquipmentUsage": "Equipment Usage",
"DND5E.ItemFeatureDetails": "Feature Details",
"DND5E.ItemFeatureSubtype": "{category} Type",
"DND5E.ItemFeatureType": "Feature Type",
"DND5E.ItemName": "Item Name",
"DND5E.ItemNew": "New {type}",
"DND5E.ItemRechargeCheck": "{name} recharge check",
Expand Down
1 change: 1 addition & 0 deletions less/items.less
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
margin: 0;
padding: 5px;
text-align: right;
white-space: nowrap;
color: @colorTan;

.item-type {
Expand Down
23 changes: 21 additions & 2 deletions module/applications/item/item-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default class ItemSheet5e extends ItemSheet {
advancementEditable: (this.advancementConfigurationMode || !item.isEmbedded) && context.editable,

// Item Type, Status, and Details
itemType: game.i18n.localize(`ITEM.Type${item.type.titleCase()}`),
itemType: this._getItemType(),
itemStatus: this._getItemStatus(),
itemProperties: this._getItemProperties(),
baseItems: await this._getItemBaseTypes(),
Expand Down Expand Up @@ -119,6 +119,7 @@ export default class ItemSheet5e extends ItemSheet {

// Set up config with proper spell components
context.config = foundry.utils.mergeObject(CONFIG.DND5E, {
featureSubtypes: CONFIG.DND5E.featureTypes[item.system.type?.value]?.subtypes,
spellComponents: {...CONFIG.DND5E.spellComponents, ...CONFIG.DND5E.spellTags}
}, {inplace: false});

Expand Down Expand Up @@ -177,6 +178,19 @@ export default class ItemSheet5e extends ItemSheet {

/* -------------------------------------------- */

/**
* Get the item type label which is shown next to the name on the top-right corner of the sheet.
* @returns {string} Localized item type.
* @protected
*/
_getItemType() {
const featureType = CONFIG.DND5E.featureTypes[this.item.system.type?.value];
if ( featureType ) return featureType.label;
return game.i18n.localize(`ITEM.Type${this.item.type.titleCase()}`);
}

/* -------------------------------------------- */

/**
* Get the base weapons and tools based on the selected type.
* @returns {Promise<object>} Object with base items for this type formatted for selectOptions.
Expand Down Expand Up @@ -277,7 +291,7 @@ export default class ItemSheet5e extends ItemSheet {
/**
* Get the text item status which is shown beneath the Item type in the top-right corner of the sheet.
* @returns {string|null} Item status string if applicable to item's type.
* @private
* @protected
*/
_getItemStatus() {
switch ( this.item.type ) {
Expand All @@ -286,11 +300,16 @@ export default class ItemSheet5e extends ItemSheet {
case "equipment":
case "weapon":
return game.i18n.localize(this.item.system.equipped ? "DND5E.Equipped" : "DND5E.Unequipped");
case "feat":
const typeConfig = CONFIG.DND5E.featureTypes[this.item.system.type.value];
if ( typeConfig?.subtypes ) return typeConfig.subtypes[this.item.system.type.subtype] ?? null;
break;
case "spell":
return CONFIG.DND5E.spellPreparationModes[this.item.system.preparation];
case "tool":
return game.i18n.localize(this.item.system.proficient ? "DND5E.Proficient" : "DND5E.NotProficient");
}
return null;
}

/* -------------------------------------------- */
Expand Down
50 changes: 50 additions & 0 deletions module/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,56 @@ preLocalize("consumableTypes", { sort: true });

/* -------------------------------------------- */

/**
* Configuration data for an item with the "feature" type.
*
* @typedef {object} FeatureTypeConfiguration
* @property {string} label Localized label for this type.
* @property {Object<string, string>} [subtypes] Enum containing localized labels for subtypes.
*/

/**
* Types of "features" items.
* @enum {FeatureTypeConfiguration}
*/
DND5E.featureTypes = {
background: {
label: "DND5E.Feature.Background"
},
class: {
label: "DND5E.Feature.Class",
subtypes: {
artificerInfusion: "DND5E.ClassFeature.ArtificerInfusion",
channelDivinity: "DND5E.ClassFeature.ChannelDivinity",
defensiveTactic: "DND5E.ClassFeature.DefensiveTactic",
eldritchInvocation: "DND5E.ClassFeature.EldritchInvocation",
elementalDiscipline: "DND5E.ClassFeature.ElementalDiscipline",
fightingStyle: "DND5E.ClassFeature.FightingStyle",
huntersPrey: "DND5E.ClassFeature.HuntersPrey",
ki: "DND5E.ClassFeature.Ki",
maneuver: "DND5E.ClassFeature.Maneuver",
metamagic: "DND5E.ClassFeature.Metamagic",
multiattack: "DND5E.ClassFeature.Multiattack",
psionicPower: "DND5E.ClassFeature.PsionicPower",
rune: "DND5E.ClassFeature.Rune",
superiorHuntersDefense: "DND5E.ClassFeature.SuperiorHuntersDefense"
}
},
monster: {
label: "DND5E.Feature.Monster"
},
species: {
label: "DND5E.Feature.Species"
},
feat: {
label: "DND5E.Feature.Feat"
}
};
preLocalize("featureTypes", { key: "label" });
preLocalize("featureTypes.class.subtypes", { sort: true });

/* -------------------------------------------- */

/**
* @typedef {object} CurrencyConfiguration
* @property {string} label Localized label for the currency.
Expand Down
7 changes: 7 additions & 0 deletions module/data/item/feat.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import ItemDescriptionTemplate from "./templates/item-description.mjs";
* @mixes ActivatedEffectTemplate
* @mixes ActionTemplate
*
* @property {object} type
* @property {string} type.value Category to which this feature belongs.
* @property {string} type.subtype Feature subtype according to its category.
* @property {string} requirements Actor details required to use this feature.
* @property {object} recharge Details on how a feature can roll for recharges.
* @property {number} recharge.value Minimum number needed to roll on a d6 to recharge this feature.
Expand All @@ -20,6 +23,10 @@ export default class FeatData extends SystemDataModel.mixin(
/** @inheritdoc */
static defineSchema() {
return this.mergeSchema(super.defineSchema(), {
type: new foundry.data.fields.SchemaField({
value: new foundry.data.fields.StringField({required: true}),
subtype: new foundry.data.fields.StringField({required: true})
}, {label: "DND5E.ItemFeatureType"}),
requirements: new foundry.data.fields.StringField({required: true, label: "DND5E.Requirements"}),
recharge: new foundry.data.fields.SchemaField({
value: new foundry.data.fields.NumberField({
Expand Down
5 changes: 5 additions & 0 deletions module/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ function _localizeObject(obj, keys) {
}
}

/* -------------------------------------------- */
/* Migration */
/* -------------------------------------------- */

/**
* Synchronize the spells for all Actors in some collection with source data from an Item compendium pack.
Expand Down Expand Up @@ -266,6 +269,8 @@ export async function synchronizeActorSpells(actorPack, spellsPack) {
SceneNavigation.displayProgressBar({label: "Synchronizing Spell Data", pct: 100});
}

/* -------------------------------------------- */

/**
* A helper function to synchronize spell data for a specific Actor.
* @param {Actor5e} actor
Expand Down
4 changes: 4 additions & 0 deletions template.json
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,10 @@
},
"feat": {
"templates": ["itemDescription", "activatedEffect", "action"],
"type": {
"value": "",
"subtype": ""
},
"requirements": "",
"recharge": {
"value": null,
Expand Down
21 changes: 21 additions & 0 deletions templates/items/feat.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@

{{!-- Details Tab --}}
<div class="tab details" data-group="primary" data-tab="details">
<h3 class="form-header">{{localize "DND5E.ItemFeatureDetails"}}</h3>

{{!-- Feature Type --}}
<div class="form-group">
<label>{{localize "DND5E.ItemFeatureType"}}</label>
<select name="system.type.value">
{{selectOptions config.featureTypes selected=system.type.value blank="" labelAttr="label"}}
</select>
</div>

{{#if config.featureSubtypes}}
<div class="form-group">
<label>
{{localize "DND5E.ItemFeatureSubtype"
category=(lookup (lookup config.featureTypes system.type.value) "label")}}
</label>
<select name="system.type.subtype">
{{selectOptions config.featureSubtypes selected=system.type.subtype blank=""}}
</select>
</div>
{{/if}}

<h3 class="form-header">{{ localize "DND5E.FeatureUsage" }}</h3>

Expand Down

0 comments on commit e14555e

Please sign in to comment.