Skip to content

Commit

Permalink
Merge pull request #2503 from arbron/types/race-data
Browse files Browse the repository at this point in the history
[#342] Add Race type, update Movement & Type config to work with items
  • Loading branch information
arbron committed Oct 31, 2023
2 parents 6dd175c + c5abf7e commit 73a7185
Show file tree
Hide file tree
Showing 21 changed files with 327 additions and 85 deletions.
12 changes: 12 additions & 0 deletions dnd5e.css
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,18 @@ h5 {
.dnd5e.sheet.item .sheet-body .item-properties [name="system.price.denomination"] {
border: none;
}
.dnd5e.sheet.item .form-group .config-button,
.dnd5e.sheet.item h4 .config-button {
color: #4b4a44;
font-size: 10px;
font-weight: normal;
opacity: 0;
}
.dnd5e.sheet.item .form-group:hover .config-button,
.dnd5e.sheet.item h4:hover .config-button {
display: inline;
opacity: 1;
}
.dnd5e.sheet.item [data-tab="description"] {
align-content: stretch;
}
Expand Down
5 changes: 5 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"ITEM.TypeFeaturePl": "Features",
"ITEM.TypeLoot": "Loot",
"ITEM.TypeLootPl": "Loot",
"ITEM.TypeRace": "Race",
"ITEM.TypeRacePl": "Races",
"ITEM.TypeSpell": "Spell",
"ITEM.TypeSpellPl": "Spells",
"ITEM.TypeSubclass": "Subclass",
Expand All @@ -57,6 +59,8 @@
"TYPES.Item.featurePl": "Features",
"TYPES.Item.loot": "Loot",
"TYPES.Item.lootPl": "Loot",
"TYPES.ITEM.race": "Race",
"TYPES.ITEM.racePl": "Races",
"TYPES.Item.spell": "Spell",
"TYPES.Item.spellPl": "Spells",
"TYPES.Item.subclass": "Subclass",
Expand Down Expand Up @@ -900,6 +904,7 @@
"DND5E.Quantity": "Quantity",
"DND5E.QuantityAbbr": "Qty",
"DND5E.Race": "Race",
"DND5E.RaceName": "Race Name",
"DND5E.RacialTraits": "Racial Traits",
"DND5E.Range": "Range",
"DND5E.RangeDistance": "Distance",
Expand Down
14 changes: 14 additions & 0 deletions less/items.less
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@
}
}

.form-group, h4 {
.config-button {
color: @colorOlive;
font-size: 10px;
font-weight: normal;
opacity: 0;
}

&:hover .config-button {
display: inline;
opacity: 1;
}
}

/* ----------------------------------------- */
/* Item Description */
/* ----------------------------------------- */
Expand Down
21 changes: 12 additions & 9 deletions module/applications/actor/movement-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,36 @@ import BaseConfigSheet from "./base-config.mjs";
*/
export default class ActorMovementConfig extends BaseConfigSheet {

/** @override */
/** @inheritdoc */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["dnd5e"],
template: "systems/dnd5e/templates/apps/movement-config.hbs",
width: 300,
height: "auto"
height: "auto",
sheetConfig: false,
keyPath: "system.attributes.movement"
});
}

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

/** @override */
/** @inheritdoc */
get title() {
return `${game.i18n.localize("DND5E.MovementConfig")}: ${this.document.name}`;
}

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

/** @override */
/** @inheritdoc */
getData(options={}) {
const source = this.document.toObject();

// Current movement values
const movement = source.system.attributes?.movement || {};
const movement = foundry.utils.getProperty(source, this.options.keyPath) ?? {};
for ( let [k, v] of Object.entries(movement) ) {
if ( ["units", "hover"].includes(k) ) continue;
movement[k] = Number.isNumeric(v) ? v.toNearest(0.1) : 0;
movement[k] = Number.isNumeric(v) ? v.toNearest(0.1) : null;
}

// Allowed speeds
Expand All @@ -52,9 +54,10 @@ export default class ActorMovementConfig extends BaseConfigSheet {
return {
speeds,
movement,
selectUnits: source.type !== "group",
canHover: source.type !== "group",
units: CONFIG.DND5E.movementUnits
selectUnits: Object.hasOwn(movement, "units"),
canHover: Object.hasOwn(movement, "hover"),
units: CONFIG.DND5E.movementUnits,
keyPath: this.options.keyPath
};
}
}
28 changes: 13 additions & 15 deletions module/applications/actor/senses-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default class ActorSensesConfig extends BaseConfigSheet {
classes: ["dnd5e"],
template: "systems/dnd5e/templates/apps/senses-config.hbs",
width: 300,
height: "auto"
height: "auto",
keyPath: "system.attributes.senses"
});
}

Expand All @@ -26,19 +27,16 @@ export default class ActorSensesConfig extends BaseConfigSheet {

/** @inheritdoc */
getData(options) {
const source = this.document.toObject().system.attributes?.senses || {};
const data = {
senses: {},
special: source.special ?? "",
units: source.units, movementUnits: CONFIG.DND5E.movementUnits
};
for ( let [name, label] of Object.entries(CONFIG.DND5E.senses) ) {
const v = Number(source[name]);
data.senses[name] = {
label: game.i18n.localize(label),
value: Number.isNumeric(v) ? v.toNearest(0.1) : 0
};
}
return data;
const source = this.document.toObject();
const senses = foundry.utils.getProperty(source, this.options.keyPath) ?? {};
return foundry.utils.mergeObject(super.getData(options), {
senses: Object.entries(CONFIG.DND5E.senses).reduce((obj, [k, label]) => {
obj[k] = { label, value: senses[k] ?? 0 };
return obj;
}, {}),
special: senses.special ?? "",
units: senses.units, movementUnits: CONFIG.DND5E.movementUnits,
keyPath: this.options.keyPath
});
}
}
21 changes: 12 additions & 9 deletions module/applications/actor/type-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import Actor5e from "../../documents/actor/actor.mjs";
/**
* A specialized form used to select from a checklist of attributes, traits, or properties
*/
export default class ActorTypeConfig extends FormApplication {
export default class ActorTypeConfig extends DocumentSheet {

/** @inheritDoc */
/** @inheritdoc */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["dnd5e", "actor-type", "trait-selector"],
Expand All @@ -15,31 +15,32 @@ export default class ActorTypeConfig extends FormApplication {
choices: {},
allowCustom: true,
minimum: 0,
maximum: null
maximum: null,
sheetConfig: false,
keyPath: "system.details.type"
});
}

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

/** @inheritDoc */
/** @inheritdoc */
get title() {
return `${game.i18n.localize("DND5E.CreatureTypeTitle")}: ${this.object.name}`;
}

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

/** @override */
/** @inheritdoc */
get id() {
return `actor-type-${this.object.id}`;
}

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

/** @override */
/** @inheritdoc */
getData(options={}) {

// Get current value or new default
let attr = foundry.utils.getProperty(this.object.system, "details.type");
let attr = foundry.utils.getProperty(this.object, this.options.keyPath);
if ( foundry.utils.getType(attr) !== "Object" ) attr = {
value: (attr in CONFIG.DND5E.creatureTypes) ? attr : "humanoid",
subtype: "",
Expand All @@ -64,6 +65,8 @@ export default class ActorTypeConfig extends FormApplication {
label: game.i18n.localize("DND5E.CreatureTypeSelectorCustom"),
chosen: attr.value === "custom"
},
showCustom: Object.hasOwn(attr, "custom"),
showSwarm: Object.hasOwn(attr, "swarm"),
subtype: attr.subtype,
swarm: attr.swarm,
sizes: Array.from(Object.entries(CONFIG.DND5E.actorSizes)).reverse().reduce((obj, e) => {
Expand All @@ -79,7 +82,7 @@ export default class ActorTypeConfig extends FormApplication {
/** @override */
async _updateObject(event, formData) {
const typeObject = foundry.utils.expandObject(formData);
return this.object.update({"system.details.type": typeObject});
return this.object.update({[this.options.keyPath]: typeObject});
}

/* -------------------------------------------- */
Expand Down
30 changes: 30 additions & 0 deletions module/applications/item/item-sheet.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import ActorMovementConfig from "../actor/movement-config.mjs";
import ActorSensesConfig from "../actor/senses-config.mjs";
import ActorTypeConfig from "../actor/type-config.mjs";
import AdvancementManager from "../advancement/advancement-manager.mjs";
import AdvancementMigrationDialog from "../advancement/advancement-migration-dialog.mjs";
import Accordion from "../accordion.mjs";
Expand Down Expand Up @@ -464,6 +467,7 @@ export default class ItemSheet5e extends ItemSheet {
super.activateListeners(html);
if ( !this.editingDescriptionTarget ) this._accordions.forEach(accordion => accordion.bind(html[0]));
if ( this.isEditable ) {
html.find(".config-button").click(this._onConfigMenu.bind(this));
html.find(".damage-control").click(this._onDamageControl.bind(this));
html.find(".trait-selector").click(this._onConfigureTraits.bind(this));
html.find(".effect-control").click(ev => {
Expand Down Expand Up @@ -529,6 +533,31 @@ export default class ItemSheet5e extends ItemSheet {
];
}

/* -------------------------------------------- */
/**
* Handle spawning the configuration applications.
* @param {Event} event The click event which originated the selection.
* @protected
*/
_onConfigMenu(event) {
event.preventDefault();
event.stopPropagation();
const button = event.currentTarget;
let app;
switch ( button.dataset.action ) {
case "movement":
app = new ActorMovementConfig(this.item, { keyPath: "system.movement" });
break;
case "senses":
app = new ActorSensesConfig(this.item, { keyPath: "system.senses" });
break;
case "type":
app = new ActorTypeConfig(this.item, { keyPath: "system.type" });
break;
}
app?.render(true);
}

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

/**
Expand Down Expand Up @@ -557,6 +586,7 @@ export default class ItemSheet5e extends ItemSheet {
return this.item.update({"system.damage.parts": damage.parts});
}
}

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

/** @inheritdoc */
Expand Down
8 changes: 2 additions & 6 deletions module/data/actor/npc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AttributesFields from "./templates/attributes.mjs";
import CreatureTemplate from "./templates/creature.mjs";
import DetailsFields from "./templates/details.mjs";
import TraitsFields from "./templates/traits.mjs";
import CreatureTypeField from "../shared/creature-type-field.mjs";

/**
* System data definition for NPCs.
Expand Down Expand Up @@ -73,12 +74,7 @@ export default class NPCData extends CreatureTemplate {
details: new foundry.data.fields.SchemaField({
...DetailsFields.common,
...DetailsFields.creature,
type: new foundry.data.fields.SchemaField({
value: new foundry.data.fields.StringField({required: true, blank: true, label: "DND5E.CreatureType"}),
subtype: new foundry.data.fields.StringField({required: true, label: "DND5E.CreatureTypeSelectorSubtype"}),
swarm: new foundry.data.fields.StringField({required: true, blank: true, label: "DND5E.CreatureSwarmSize"}),
custom: new foundry.data.fields.StringField({required: true, label: "DND5E.CreatureTypeSelectorCustom"})
}, {label: "DND5E.CreatureType"}),
type: new CreatureTypeField(),
environment: new foundry.data.fields.StringField({required: true, label: "DND5E.Environment"}),
cr: new foundry.data.fields.NumberField({
required: true, nullable: false, min: 0, initial: 1, label: "DND5E.ChallengeRating"
Expand Down
39 changes: 4 additions & 35 deletions module/data/actor/templates/attributes.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { FormulaField } from "../../fields.mjs";
import MovementField from "../../shared/movement-field.mjs";
import SensesField from "../../shared/senses-field.mjs";

/**
* Shared contents of the attributes schema between various actor types.
Expand Down Expand Up @@ -26,25 +28,7 @@ export default class AttributesFields {
ability: new foundry.data.fields.StringField({label: "DND5E.AbilityModifier"}),
bonus: new FormulaField({label: "DND5E.InitiativeBonus"})
}, { label: "DND5E.Initiative" }),
movement: new foundry.data.fields.SchemaField({
burrow: new foundry.data.fields.NumberField({
nullable: false, min: 0, step: 0.1, initial: 0, label: "DND5E.MovementBurrow"
}),
climb: new foundry.data.fields.NumberField({
nullable: false, min: 0, step: 0.1, initial: 0, label: "DND5E.MovementClimb"
}),
fly: new foundry.data.fields.NumberField({
nullable: false, min: 0, step: 0.1, initial: 0, label: "DND5E.MovementFly"
}),
swim: new foundry.data.fields.NumberField({
nullable: false, min: 0, step: 0.1, initial: 0, label: "DND5E.MovementSwim"
}),
walk: new foundry.data.fields.NumberField({
nullable: false, min: 0, step: 0.1, initial: 30, label: "DND5E.MovementWalk"
}),
units: new foundry.data.fields.StringField({initial: "ft", label: "DND5E.MovementUnits"}),
hover: new foundry.data.fields.BooleanField({label: "DND5E.MovementHover"})
}, {label: "DND5E.Movement"})
movement: new MovementField()
};
}

Expand Down Expand Up @@ -72,22 +56,7 @@ export default class AttributesFields {
required: true, nullable: false, integer: true, min: 0, initial: 3, label: "DND5E.AttunementMax"
})
}, {label: "DND5E.Attunement"}),
senses: new foundry.data.fields.SchemaField({
darkvision: new foundry.data.fields.NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.SenseDarkvision"
}),
blindsight: new foundry.data.fields.NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.SenseBlindsight"
}),
tremorsense: new foundry.data.fields.NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.SenseTremorsense"
}),
truesight: new foundry.data.fields.NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.SenseTruesight"
}),
units: new foundry.data.fields.StringField({required: true, initial: "ft", label: "DND5E.SenseUnits"}),
special: new foundry.data.fields.StringField({required: true, label: "DND5E.SenseSpecial"})
}, {label: "DND5E.Senses"}),
senses: new SensesField(),
spellcasting: new foundry.data.fields.StringField({
required: true, blank: true, initial: "int", label: "DND5E.SpellAbility"
})
Expand Down
3 changes: 3 additions & 0 deletions module/data/item/_module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ContainerData from "./container.mjs";
import EquipmentData from "./equipment.mjs";
import FeatData from "./feat.mjs";
import LootData from "./loot.mjs";
import RaceData from "./race.mjs";
import SpellData from "./spell.mjs";
import SubclassData from "./subclass.mjs";
import ToolData from "./tool.mjs";
Expand All @@ -18,6 +19,7 @@ export {
EquipmentData,
FeatData,
LootData,
RaceData,
SpellData,
SubclassData,
ToolData,
Expand All @@ -38,6 +40,7 @@ export const config = {
equipment: EquipmentData,
feat: FeatData,
loot: LootData,
race: RaceData,
spell: SpellData,
subclass: SubclassData,
tool: ToolData,
Expand Down
Loading

0 comments on commit 73a7185

Please sign in to comment.