Skip to content

Commit

Permalink
[foundryvtt#1871] Add starting equipment data
Browse files Browse the repository at this point in the history
  • Loading branch information
arbron committed Feb 29, 2024
1 parent f9c6bb9 commit 1224474
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 15 deletions.
1 change: 1 addition & 0 deletions icons/LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ The dnd5e system for Foundry Virtual Tabletop includes icon artwork licensed fro
/svg/item-grant.svg - "White book" by Willdabeast under CC BY 3.0
/svg/scale-value.svg - "Dice target" by Delapouite under CC BY 3.0
/svg/size.svg - "Body height" by Delapouite under CC BY 3.0
/svg/starting-equipment.svg - "Battle gear" by Lorc under CC BY 3.0
/svg/trait.svg - "Scroll unfurled" by Lorc under CC BY 3.0
/svg/trait-armor-proficiencies.svg - "Leather armor" by Delapouite under CC BY 3.0
/svg/trait-damage-immunities.svg - "Aura" by Lorc under CC BY 3.0
Expand Down
1 change: 1 addition & 0 deletions icons/svg/starting-equipment.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
"DND5E.AdvancementConfiguredComplete": "Fully Configured",
"DND5E.AdvancementConfiguredIncomplete": "Not Configured",
"DND5E.AdvancementControlCreate": "Create Advancement",
"DND5E.AdvancementControlConfigure": "Configure {name}",
"DND5E.AdvancementControlDelete": "Delete Advancement",
"DND5E.AdvancementControlDuplicate": "Duplicate Advancement",
"DND5E.AdvancementControlEdit": "Edit Advancement",
Expand Down Expand Up @@ -1373,6 +1374,25 @@
"DND5E.SpellUnprepared": "Unprepared",
"DND5E.SpellUsage": "Spell Usage",
"DND5E.Spellbook": "Spellbook",
"DND5E.StartingEquipment": {
"Title": "Starting Equipment",
"Choice": {
"Armor": "Choose Armor",
"Focus": "Choose Spellcasting Focus",
"Tool": "Choose Tool",
"Weapon": "Choose Weapon"
},
"IfProficient": "If Proficient",
"Operator": {
"AND": "All",
"OR": "One"
},
"SpecificItem": "Specific Item",
"Wealth": {
"Label": "Starting Wealth",
"Hint": "Formula in GP that can be used in place of starting equipment."
}
},
"DND5E.SubclassIdentifierHint": "This identifier should match the identifier on the parent class to ensure they are properly linked.",
"DND5E.SubclassAssignmentError": "{class} already has a subclass. Remove the existing '{subclass}' subclass before adding a new one.",
"DND5E.SubclassDuplicateError": "A subclass with the identifier {identifier} already exists on this actor.",
Expand Down Expand Up @@ -1589,6 +1609,7 @@
"DND5E.WarnCantAddMultipleAdvancements": "It is not currently possible to add multiple items with advancements to an actor at the same time. Please add them individually.",
"DND5E.WarnMultipleArmor": "More than one suit of armor equipped, AC calculation may be incorrect.",
"DND5E.WarnMultipleShields": "More than one shield equipped, AC calculation may be incorrect.",
"DND5E.WeaponCategory": "{category} Weapon",
"DND5E.WeaponImprov": "Improvised",
"DND5E.WeaponMartialM": "Martial Melee",
"DND5E.WeaponMartialProficiency": "Martial",
Expand Down
1 change: 1 addition & 0 deletions module/applications/advancement/_module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export {default as ScaleValueConfig} from "./scale-value-config.mjs";
export {default as ScaleValueFlow} from "./scale-value-flow.mjs";
export {default as SizeConfig} from "./size-config.mjs";
export {default as SizeFlow} from "./size-flow.mjs";
export {default as StartingEquipmentConfig} from "./starting-equipment-config.mjs";
export {default as TraitConfig} from "./trait-config.mjs";
export {default as TraitFlow} from "./trait-flow.mjs";
23 changes: 23 additions & 0 deletions module/applications/advancement/starting-equipment-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import AdvancementConfig from "./advancement-config.mjs";

/**
* Configuration application for Starting Equipment.
*/
export default class StartingEquipmentConfig extends AdvancementConfig {

/** @inheritdoc */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["dnd5e", "advancement", "starting-equipment"],
template: "systems/dnd5e/templates/advancement/starting-equipment-config.hbs"
});
}

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

/** @inheritdoc */
getData(options={}) {
const context = super.getData(options);
return context;
}
}
1 change: 1 addition & 0 deletions module/applications/item/_module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export {default as ItemDirectory5e} from "./item-directory.mjs";
export {default as ItemSheet5e} from "./item-sheet.mjs";

export {default as AbilityUseDialog} from "./ability-use-dialog.mjs";
export {default as StartingEquipmentConfig} from "./starting-equipment-config.mjs";
export {default as SummoningConfig} from "./summoning-config.mjs";
7 changes: 7 additions & 0 deletions module/applications/item/item-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AdvancementMigrationDialog from "../advancement/advancement-migration-dia
import Accordion from "../accordion.mjs";
import EffectsElement from "../components/effects.mjs";
import SourceConfig from "../source-config.mjs";
import StartingEquipmentConfig from "./starting-equipment-config.mjs";
import SummoningConfig from "./summoning-config.mjs";

/**
Expand Down Expand Up @@ -203,6 +204,8 @@ export default class ItemSheet5e extends ItemSheet {
};
}

const supplementalAdvancement = item.system.supplementalAdvancement ?? {};

// All other advancements by level
for ( let [level, advancements] of Object.entries(item.advancement.byLevel) ) {
if ( !configMode ) advancements = advancements.filter(a => a.appliesToClass);
Expand All @@ -215,6 +218,7 @@ export default class ItemSheet5e extends ItemSheet {
summary: advancement.summaryForLevel(level, { configMode }),
configured: advancement.configuredForLevel(level)
}));
items.push(...(supplementalAdvancement[level] ?? []));
if ( !items.length ) continue;
advancement[level] = {
items: items.sort((a, b) => a.order.localeCompare(b.order, game.i18n.lang)),
Expand Down Expand Up @@ -562,6 +566,9 @@ export default class ItemSheet5e extends ItemSheet {
case "source":
app = new SourceConfig(this.item, { keyPath: "system.source" });
break;
case "starting-equipment":
app = new StartingEquipmentConfig(this.item);
break;
case "summoning":
app = new SummoningConfig(this.item);
break;
Expand Down
40 changes: 40 additions & 0 deletions module/applications/item/starting-equipment-config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Configuration application for Starting Equipment.
*/
export default class StartingEquipmentConfig extends DocumentSheet {

/** @inheritDoc */
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["dnd5e", "starting-equipment"],
dragDrop: [{ dropSelector: "form" }],
template: "systems/dnd5e/templates/apps/starting-equipment-config.hbs",
width: 400,
height: "auto",
sheetConfig: false,
closeOnSubmit: false,
submitOnChange: true,
submitOnClose: true
});
}

/* -------------------------------------------- */
/* Rendering */
/* -------------------------------------------- */

/** @inheritDoc */
async getData(options={}) {
const context = await super.getData(options);
context.startingEquipment = this.document.system.startingEquipment;
return context;
}

/* -------------------------------------------- */
/* Event Handling */
/* -------------------------------------------- */

/** @inheritDoc */
async _updateObject(event, formData) {
console.log(event, formData);
}
}
1 change: 1 addition & 0 deletions module/data/item/_module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {default as ItemDescriptionTemplate} from "./templates/item-description.m
export {default as ItemTypeTemplate} from "./templates/item-type.mjs";
export {default as MountableTemplate} from "./templates/mountable.mjs";
export {default as PhysicalItemTemplate} from "./templates/physical-item.mjs";
export * as startingEquipment from "./templates/starting-equipment.mjs";

export const config = {
background: BackgroundData,
Expand Down
16 changes: 15 additions & 1 deletion module/data/item/background.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { ItemDataModel } from "../abstract.mjs";
import { AdvancementField } from "../fields.mjs";
import ItemDescriptionTemplate from "./templates/item-description.mjs";
import StartingEquipmentTemplate from "./templates/starting-equipment.mjs";

/**
* Data definition for Background items.
* @mixes ItemDescriptionTemplate
* @mixes StartingEquipmentTemplate
*
* @property {object[]} advancement Advancement objects for this background.
*/
export default class BackgroundData extends ItemDataModel.mixin(ItemDescriptionTemplate) {
export default class BackgroundData extends ItemDataModel.mixin(ItemDescriptionTemplate, StartingEquipmentTemplate) {
/** @inheritdoc */
static defineSchema() {
return this.mergeSchema(super.defineSchema(), {
Expand All @@ -23,6 +25,18 @@ export default class BackgroundData extends ItemDataModel.mixin(ItemDescriptionT
singleton: true
}, {inplace: false}));

/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */

/**
* Fetch additional advancement entries.
* @type {object[]}
*/
get supplementalAdvancement() {
return { 1: [this.startingEquipmentAdvancement] };
}

/* -------------------------------------------- */
/* Socket Event Handlers */
/* -------------------------------------------- */
Expand Down
38 changes: 28 additions & 10 deletions module/data/item/class.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import TraitAdvancement from "../../documents/advancement/trait.mjs";
import { ItemDataModel } from "../abstract.mjs";
import { AdvancementField, IdentifierField } from "../fields.mjs";
import { AdvancementField, FormulaField, IdentifierField } from "../fields.mjs";
import ItemDescriptionTemplate from "./templates/item-description.mjs";
import StartingEquipmentTemplate from "./templates/starting-equipment.mjs";

const { ArrayField, NumberField, SchemaField, StringField } = foundry.data.fields;

/**
* Data definition for Class items.
* @mixes ItemDescriptionTemplate
* @mixes StartingEquipmentTemplate
*
* @property {string} identifier Identifier slug for this class.
* @property {number} levels Current number of levels in this class.
Expand All @@ -15,32 +19,46 @@ import ItemDescriptionTemplate from "./templates/item-description.mjs";
* @property {object} spellcasting Details on class's spellcasting ability.
* @property {string} spellcasting.progression Spell progression granted by class as from `DND5E.spellProgression`.
* @property {string} spellcasting.ability Ability score to use for spellcasting.
* @property {string} wealth Formula used to determine starting wealth.
*/
export default class ClassData extends ItemDataModel.mixin(ItemDescriptionTemplate) {
export default class ClassData extends ItemDataModel.mixin(ItemDescriptionTemplate, StartingEquipmentTemplate) {
/** @inheritdoc */
static defineSchema() {
return this.mergeSchema(super.defineSchema(), {
identifier: new IdentifierField({required: true, label: "DND5E.Identifier"}),
levels: new foundry.data.fields.NumberField({
levels: new NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 1, label: "DND5E.ClassLevels"
}),
hitDice: new foundry.data.fields.StringField({
hitDice: new StringField({
required: true, initial: "d6", blank: false, label: "DND5E.HitDice",
validate: v => /d\d+/.test(v), validationError: "must be a dice value in the format d#"
}),
hitDiceUsed: new foundry.data.fields.NumberField({
hitDiceUsed: new NumberField({
required: true, nullable: false, integer: true, initial: 0, min: 0, label: "DND5E.HitDiceUsed"
}),
advancement: new foundry.data.fields.ArrayField(new AdvancementField(), {label: "DND5E.AdvancementTitle"}),
spellcasting: new foundry.data.fields.SchemaField({
progression: new foundry.data.fields.StringField({
advancement: new ArrayField(new AdvancementField(), {label: "DND5E.AdvancementTitle"}),
spellcasting: new SchemaField({
progression: new StringField({
required: true, initial: "none", blank: false, label: "DND5E.SpellProgression"
}),
ability: new foundry.data.fields.StringField({required: true, label: "DND5E.SpellAbility"})
}, {label: "DND5E.Spellcasting"})
ability: new StringField({required: true, label: "DND5E.SpellAbility"})
}, {label: "DND5E.Spellcasting"}),
wealth: new FormulaField({label: "DND5E.StartingEquipment.Wealth.Label"})
});
}

/* -------------------------------------------- */
/* Properties */
/* -------------------------------------------- */

/**
* Fetch additional advancement entries.
* @type {object[]}
*/
get supplementalAdvancement() {
return { 1: [this.startingEquipmentAdvancement] };
}

/* -------------------------------------------- */
/* Data Preparation */
/* -------------------------------------------- */
Expand Down
Loading

0 comments on commit 1224474

Please sign in to comment.