diff --git a/langs/en-ca.2.json b/langs/en-ca.2.json index f8a1ab5..dce0efa 100644 --- a/langs/en-ca.2.json +++ b/langs/en-ca.2.json @@ -101,32 +101,6 @@ "title": "Delete Effect", "content": "

Are you sure you would like to delete the active effect: {name}

" } - }, - "settings": { - "showAvatarOnSheet": { - "name": "Show Avatar On Player Sheet", - "description": "Determines whether or not to show the avatar to you on the Player Character sheets, turning this off will replace the image with a file picker so that you can still change the image from the character sheet." - }, - "playersCanChangeGroup": { - "name": "Allow Players to Change Group", - "description": "Setting this to true allows non-GM players to modify the group that the Actor belongs to. While this is disabled the GM will still be able to modify each player's group by editing the character sheet." - }, - "resourcesOrSupplies": { - "name": "Use Resources or Supplies", - "description": "Determines which term to use for the objects that allow travelling into the next hex tile. This is because of the", - "option": { - "supplies": "Supplies", - "resources": "Resources" - } - }, - "openEmbeddedOnCreate": { - "name": "Edit Custom Items Immediately When Created", - "description": "Tells the character sheets that have \"Add\" buttons to open the Item's sheet when you create the new item so that you can immediately edit it without needing to click more buttons." - }, - "aspectLimit": { - "name": "Character Aspect Limit", - "description": "Limit how many Aspects a single character can have." - } } }, "TYPES": { diff --git a/module/components/icon.mjs b/module/components/icon.mjs index 82c8e99..8c70d40 100644 --- a/module/components/icon.mjs +++ b/module/components/icon.mjs @@ -4,8 +4,6 @@ import { StyledShadowElement } from "./mixins/Styles.mjs"; Attributes: @property {string} name - The name of the icon, takes precedence over the path @property {string} path - The path of the icon file - -@extends {HTMLElement} */ export class DotDungeonIcon extends StyledShadowElement(HTMLElement) { static elementName = `dd-icon`; diff --git a/module/components/incrementer.mjs b/module/components/incrementer.mjs index 0c15387..68e426a 100644 --- a/module/components/incrementer.mjs +++ b/module/components/incrementer.mjs @@ -13,14 +13,8 @@ Attributes: Styling: - `--height`: Controls the height of the element + the width of the buttons (default: 1.25rem) - `--width`: Controls the width of the number input (default 50px) - -@extends {HTMLElement} */ -export class DotDungeonIncrementer -extends StyledShadowElement( - HTMLElement, - { mode: `open`, delegatesFocus: true } -) { +export class DotDungeonIncrementer extends StyledShadowElement(HTMLElement) { static elementName = `dd-incrementer`; static formAssociated = true; @@ -44,14 +38,14 @@ extends StyledShadowElement( get form() { return this._internals.form; - }; + } get name() { return this.getAttribute(`name`); - }; + } set name(value) { this.setAttribute(`name`, value); - }; + } get value() { return this.getAttribute(`value`); @@ -62,7 +56,7 @@ extends StyledShadowElement( get type() { return `number`; - }; + } connectedCallback() { super.connectedCallback(); diff --git a/module/components/index.mjs b/module/components/index.mjs index 7d870b0..f4d39e9 100644 --- a/module/components/index.mjs +++ b/module/components/index.mjs @@ -1,11 +1,9 @@ import { DotDungeonIncrementer } from "./incrementer.mjs"; import { DotDungeonIcon } from "./icon.mjs"; -import { DotDungeonRange } from "./range.mjs"; const components = [ DotDungeonIcon, DotDungeonIncrementer, - DotDungeonRange, ]; export function registerCustomComponents() { diff --git a/module/components/mixins/Styles.mjs b/module/components/mixins/Styles.mjs index bbceaad..33d5eb5 100644 --- a/module/components/mixins/Styles.mjs +++ b/module/components/mixins/Styles.mjs @@ -1,7 +1,7 @@ /** * @param {HTMLElement} Base */ -export function StyledShadowElement(Base, shadowOptions = { mode: `open` }) { +export function StyledShadowElement(Base) { return class extends Base { /** * The path to the CSS that is loaded @@ -33,7 +33,7 @@ export function StyledShadowElement(Base, shadowOptions = { mode: `open` }) { constructor() { super(); - this._shadow = this.attachShadow(shadowOptions); + this._shadow = this.attachShadow({ mode: `open` }); this._style = document.createElement(`style`); this._shadow.appendChild(this._style); }; diff --git a/module/components/range.mjs b/module/components/range.mjs deleted file mode 100644 index db286fe..0000000 --- a/module/components/range.mjs +++ /dev/null @@ -1,139 +0,0 @@ -import { DotDungeonIcon } from "./icon.mjs"; -import { StyledShadowElement } from "./mixins/Styles.mjs"; - -/** -Attributes: -@property {string} name - The path to the value to update in the datamodel -@property {number} value - The actual value of the input -@property {number} max - The maximum value that this range has - -@extends {HTMLElement} -*/ -export class DotDungeonRange -extends StyledShadowElement( - HTMLElement, - { mode: `open`, delegatesFocus: true } -) { - static elementName = `dd-range`; - static formAssociated = true; - - static observedAttributes = [`max`]; - - static _stylePath = `v3/components/range.css`; - - _internals; - #input; - - constructor() { - super(); - - // Form internals - this._internals = this.attachInternals(); - this._internals.role = `spinbutton`; - }; - - get form() { - return this._internals.form; - }; - - get name() { - return this.getAttribute(`name`); - }; - set name(value) { - this.setAttribute(`name`, value); - }; - - get value() { - try { - return parseInt(this.getAttribute(`value`)); - } catch { - throw new Error(`Failed to parse attribute: "value" - Make sure it's an integer`); - }; - }; - set value(value) { - this.setAttribute(`value`, value); - }; - - get max() { - try { - return parseInt(this.getAttribute(`max`)); - } catch { - throw new Error(`Failed to parse attribute: "max" - Make sure it's an integer`); - }; - }; - set max(value) { - this.setAttribute(`max`, value); - }; - - get type() { - return `number`; - }; - - connectedCallback() { - super.connectedCallback(); - - // Attribute validation - if (!this.hasAttribute(`max`)) { - throw new Error(`dotdungeon | Cannot have a range without a maximum value`); - }; - - // Keyboard accessible input for the thing - this.#input = document.createElement(`input`); - this.#input.type = `number`; - this.#input.min = 0; - this.#input.max = this.max; - this.#input.value = this.value; - this.#input.addEventListener(`change`, () => { - const inputValue = parseInt(this.#input.value); - if (inputValue === this.value) return; - this._updateValue.bind(this)(Math.sign(this.value - inputValue)); - this._updateValue(Math.sign(this.value - inputValue)); - }); - this._shadow.appendChild(this.#input); - - // Shadow-DOM construction - this._elements = new Array(this.max); - const container = document.createElement(`div`); - container.classList.add(`container`); - - // Creating the node for filled content - const filledContainer = document.createElement(`div`); - filledContainer.classList.add(`range-increment`, `filled`); - const filledNode = this.querySelector(`[slot="filled"]`); - if (filledNode) filledContainer.appendChild(filledNode); - - const emptyContainer = document.createElement(`div`); - emptyContainer.classList.add(`range-increment`, `empty`); - const emptyNode = this.querySelector(`[slot="empty"]`); - if (emptyNode) emptyContainer.appendChild(emptyNode); - - this._elements.fill(filledContainer, 0, this.value); - this._elements.fill(emptyContainer, this.value); - container.append(...this._elements.map((slot, i) => { - const node = slot.cloneNode(true); - node.setAttribute(`data-index`, i + 1); - node.addEventListener(`click`, () => { - const filled = node.classList.contains(`filled`); - this._updateValue(filled ? -1 : 1); - }); - return node; - })); - this._shadow.appendChild(container); - - /* - This converts all of the namespace prefixed properties on the element to - CSS variables so that they don't all need to be provided by doing style="" - */ - for (const attrVar of this.attributes) { - if (attrVar.name?.startsWith(`var:`)) { - const prop = attrVar.name.replace(`var:`, ``); - this.style.setProperty(`--` + prop, attrVar.value); - }; - }; - }; - - _updateValue(delta) { - this.value += delta; - this.dispatchEvent(new Event(`change`, { bubbles: true })); - }; -}; diff --git a/module/config.mjs b/module/config.mjs index d1fa118..56a0fb4 100644 --- a/module/config.mjs +++ b/module/config.mjs @@ -68,11 +68,6 @@ export const itemFilters = [ `service`, ]; -export const invalidActiveEffectTargets = new Set([ - `system.uses_inventory_slot`, - `system.quantity_affects_used_capacity`, -]); - export default { stats, statDice, @@ -91,5 +86,4 @@ export default { syncDice, localizerConfig, itemFilters, - invalidActiveEffectTargets, }; diff --git a/module/documents/ActiveEffect/GenericActiveEffect.mjs b/module/documents/ActiveEffect/GenericActiveEffect.mjs index 47f10d3..8ee70f3 100644 --- a/module/documents/ActiveEffect/GenericActiveEffect.mjs +++ b/module/documents/ActiveEffect/GenericActiveEffect.mjs @@ -1,25 +1,7 @@ -import { invalidActiveEffectTargets } from "../../config.mjs"; - export class DotDungeonActiveEffect extends ActiveEffect { // Invert the logic of the disabled property so it's easier to modify via // embedded controls get enabled() { return !this.disabled }; set enabled(newValue) { this.disabled = !newValue }; - - apply(object, change) { - if (invalidActiveEffectTargets.has(change.key)) return; - - change.value = change.value.replace( - /@(?[\w\.]+)/gi, - (...args) => { - const key = args[1]; - if (foundry.utils.hasProperty(object, key)) { - return foundry.utils.getProperty(object, key) - }; - return args[0]; - } - ) - return super.apply(object, change); - } }; diff --git a/module/documents/Actor/GenericActor.mjs b/module/documents/Actor/GenericActor.mjs index feb3825..171af63 100644 --- a/module/documents/Actor/GenericActor.mjs +++ b/module/documents/Actor/GenericActor.mjs @@ -6,7 +6,7 @@ export class DotDungeonActor extends Actor { provide all that data to AE's without needing to disable any inputs. */ prepareEmbeddedDocuments() { - this.preAE = foundry.utils.duplicate(this.system); + this.preAE = foundry.utils.deepClone(this.system); super.prepareEmbeddedDocuments(); }; diff --git a/module/documents/Actor/Player.mjs b/module/documents/Actor/Player.mjs index 7430fa1..2c5840d 100644 --- a/module/documents/Actor/Player.mjs +++ b/module/documents/Actor/Player.mjs @@ -2,17 +2,6 @@ import { DotDungeonActor } from "./GenericActor.mjs"; export class Player extends DotDungeonActor { - /* - These are special data properties that will be used by ActiveEffects to modify - certain limits within the actor, allowing for neat hacks that change these and - possible configuration of them. - */ - prepareBaseData() { - this.system.weapon_slots = 2; - this.system.inventory_slots = 0; - this.system.respawn_limit = 3; - }; - applyActiveEffects() { super.applyActiveEffects(); diff --git a/module/dotdungeon.mjs b/module/dotdungeon.mjs index 16eb6dd..8731314 100644 --- a/module/dotdungeon.mjs +++ b/module/dotdungeon.mjs @@ -1,5 +1,6 @@ // Data Models import { DescribedItemData } from "./models/Item/DescribedItemData.mjs"; +import { CommonItemData } from "./models/Item/CommonItemData.mjs"; import { WeaponItemData } from "./models/Item/Weapon.mjs"; import { AspectItemData } from "./models/Item/Aspect.mjs"; import { SpellItemData } from "./models/Item/Spell.mjs"; @@ -15,7 +16,6 @@ import { ItemProxy } from "./documents/Item/_proxy.mjs"; // Item Sheets import { UntypedItemSheet } from "./sheets/Items/UntypedItemSheet.mjs"; -import { WeaponSheet } from "./sheets/Items/WeaponSheet.mjs"; import { AspectSheet } from "./sheets/Items/AspectSheet.mjs"; import { SpellSheet } from "./sheets/Items/SpellSheet.mjs"; import { PetSheet } from "./sheets/Items/PetSheet.mjs"; @@ -39,7 +39,6 @@ import { devInit } from "./hooks/devInit.mjs"; import DOTDUNGEON from "./config.mjs"; -// MARK: init hook Hooks.once(`init`, async () => { console.debug(`.dungeon | Initializing`); CONFIG.ActiveEffect.legacyTransferral = false; @@ -50,7 +49,7 @@ Hooks.once(`init`, async () => { CONFIG.Actor.dataModels.sync = SyncData; CONFIG.Actor.dataModels.mob = MobData; CONFIG.Item.dataModels.untyped = DescribedItemData; - CONFIG.Item.dataModels.material = DescribedItemData; + CONFIG.Item.dataModels.material = CommonItemData; CONFIG.Item.dataModels.foil = DescribedItemData; CONFIG.Item.dataModels.weapon = WeaponItemData; CONFIG.Item.dataModels.aspect = AspectItemData; @@ -96,11 +95,6 @@ Hooks.once(`init`, async () => { types: ["aspect"], label: "dotdungeon.sheet-names.AspectSheet" }); - Items.registerSheet("dotdungeon", WeaponSheet, { - makeDefault: true, - types: ["weapon"], - label: "dotdungeon.sheet-names.WeaponSheet" - }); Items.registerSheet("dotdungeon", SpellSheet, { makeDefault: true, types: ["spell"], @@ -122,7 +116,6 @@ Hooks.once(`init`, async () => { }); -// MARK: ready hook Hooks.once(`ready`, () => { console.debug(".dungeon | Ready"); diff --git a/module/handlebars.mjs b/module/handlebars.mjs index 76e3f44..cdaa1b7 100644 --- a/module/handlebars.mjs +++ b/module/handlebars.mjs @@ -32,15 +32,11 @@ export const partials = [ `actors/char-sheet/v2/partials/inventory/items/weapon.v2.pc.hbs`, `actors/char-sheet/v2/partials/inventory/items/pet.v2.pc.hbs`, - // The partials used for Untyped v2 and other item sheets that don't have a - // unique design for the other tabs + // The v2 Untyped sheet partials `items/untyped/v2/tabs/general.v2.untyped.hbs`, `items/untyped/v2/tabs/details.v2.untyped.hbs`, `items/untyped/v2/tabs/effects.v2.untyped.hbs`, `items/untyped/v2/tabs/settings.v2.untyped.hbs`, - - // The weapon sheet partials - `items/weapon/v1/tabs/details.v1.weapon.hbs`, ]; export const preAliasedPartials = { diff --git a/module/helpers/index.mjs b/module/helpers/index.mjs index 5c24614..b48baa3 100644 --- a/module/helpers/index.mjs +++ b/module/helpers/index.mjs @@ -7,7 +7,7 @@ import { options } from "./options.mjs"; export default { - // MARK: Complex helpers + // Complex helpers "dd-schemaOptions": schemaOptions, "dd-array": createArray, "dd-objectValue": objectValue, @@ -15,13 +15,13 @@ export default { "dd-i18n": handlebarsLocalizer, "dd-options": options, - // MARK: Simple helpers + // Simple helpers "dd-stringify": v => JSON.stringify(v, null, ` `), "dd-empty": v => v.length == 0, "dd-set-has": (s, k) => s.has(k), "dd-empty-state": (v) => v ?? localizer(`dotdungeon.common.empty`), - // MARK: Logic helpers + // Logic helpers "eq": (a, b) => a == b, "neq": (a, b) => a != b, "not": v => !v, diff --git a/module/models/Actor/Player.mjs b/module/models/Actor/Player.mjs index 1b71b47..244c887 100644 --- a/module/models/Actor/Player.mjs +++ b/module/models/Actor/Player.mjs @@ -1,5 +1,15 @@ import DOTDUNGEON from "../../config.mjs"; -import { DiceField } from "../fields/DiceField.mjs"; + +function diceChoiceField() { + return new foundry.data.fields.StringField({ + initial: ``, + blank: true, + trim: true, + options() { + return DOTDUNGEON.statDice; + }, + }); +}; function trainingLevelField() { return new foundry.data.fields.NumberField({ @@ -14,18 +24,26 @@ export class PlayerData extends foundry.abstract.TypeDataModel { static defineSchema() { const fields = foundry.data.fields; return { + /* + These are special data properties that will be used by ActiveEffects + to modify certain limits within the actor, allowing for neat hacks + that change these + */ + weapon_slots: new fields.NumberField({ initial: 2 }), + inventory_slots: new fields.NumberField({ initial: 0 }), + bytes: new fields.NumberField({ initial: 0, min: 0, integer: true, }), stats: new fields.SchemaField({ - build: new DiceField(), - meta: new DiceField(), - presence: new DiceField(), - hands: new DiceField(), - tilt: new DiceField(), - rng: new DiceField(), + build: diceChoiceField(), + meta: diceChoiceField(), + presence: diceChoiceField(), + hands: diceChoiceField(), + tilt: diceChoiceField(), + rng: diceChoiceField(), }), skills: new fields.SchemaField({ build: new fields.SchemaField({ @@ -69,7 +87,11 @@ export class PlayerData extends foundry.abstract.TypeDataModel { min: 0, integer: true }), - respawns: new fields.NumberField({ initial: 0, }), + respawns: new fields.SchemaField({ + r1: new fields.BooleanField(), + r2: new fields.BooleanField(), + r3: new fields.BooleanField(), + }), syncDelta: new fields.NumberField({ required: true, integer: true, diff --git a/module/models/fields/DiceField.mjs b/module/models/fields/DiceField.mjs deleted file mode 100644 index e147fb2..0000000 --- a/module/models/fields/DiceField.mjs +++ /dev/null @@ -1,88 +0,0 @@ -import { statDice } from "../../config.mjs"; - -/** - * A subclass of DataField that allows ActiveEffects to integrate with dice - * values and increase/decrease the value step-wise according to the dice ladder. - */ -export class DiceField extends foundry.data.fields.DataField { - static get _defaults() { - return foundry.utils.mergeObject(super._defaults, { - trim: true, - blank: true, - initial: ``, - choices: [``, ...statDice], - }); - }; - - constructor(options = {}, context = {}) { - super(options, context); - - // v- because for some reason Foundry doesn't respect the _defaults getter - this.blank = true; - }; - - _cast(value) { return value }; - _castChangeDelta(delta) { return delta }; - - /** - * @param {string} value The current value - * @param {string} delta The AE value - * @param {unknown} model - * @param {unknown} changes - */ - _applyChangeAdd(value, delta, model, changes) { - if (value === "") return value; - delta = parseInt(delta); - const dieIndex = statDice.findIndex(die => die === value); - const newIndex = Math.min(Math.max(0, dieIndex + delta), statDice.length - 1); - value = statDice[newIndex]; - return value; - }; - - /** - * @param {string} value The current value - * @param {string} delta The AE value - * @param {unknown} model - * @param {unknown} changes - */ - _applyChangeMultiply(value, delta, model, changes) { - console.warn(`.dungeon | Cannot apply Multiply ActiveEffects to DiceFields. Not changing value.`); - return value; - }; - - /** - * @param {string} value The current value - * @param {string} delta The AE value - * @param {unknown} model - * @param {unknown} changes - */ - _applyChangeOverride(value, delta, model, changes) { - return delta; - }; - - /** - * @param {string} value The current value - * @param {string} delta The AE value - * @param {unknown} model - * @param {unknown} changes - */ - _applyChangeUpgrade(value, delta, model, changes) { - if (value === "") return value; - const currentIndex = statDice.findIndex(die => die === value); - const upgradedIndex = statDice.findIndex(die => die === delta); - return statDice[Math.max(currentIndex, upgradedIndex)]; - }; - - /** - * @param {string} value The current value - * @param {string} delta The AE value - * @param {unknown} model - * @param {unknown} changes - */ - _applyChangeDowngrade(value, delta, model, changes) { - if (value === "") return value; - const currentIndex = statDice.findIndex(die => die === value); - const upgradedIndex = statDice.findIndex(die => die === delta); - return statDice[Math.min(currentIndex, upgradedIndex)]; - }; -}; diff --git a/module/sheets/Actors/PC/PlayerSheetV2.mjs b/module/sheets/Actors/PC/PlayerSheetV2.mjs index 466e6c5..ac1a378 100644 --- a/module/sheets/Actors/PC/PlayerSheetV2.mjs +++ b/module/sheets/Actors/PC/PlayerSheetV2.mjs @@ -92,7 +92,7 @@ export class PlayerSheetv2 extends GenericActorSheet { get #statData() { const stats = []; - const usedDice = new Set(Object.values(this.actor.preAE.stats)); + const usedDice = new Set(Object.values(this.actor.system.stats)); for (const statName in this.actor.system.stats) { const stat = { key: statName, diff --git a/module/sheets/GenericActorSheet.mjs b/module/sheets/GenericActorSheet.mjs index ba668db..2010304 100644 --- a/module/sheets/GenericActorSheet.mjs +++ b/module/sheets/GenericActorSheet.mjs @@ -58,7 +58,7 @@ export class GenericActorSheet extends ActorSheet { */ html.find( CONFIG.CACHE.componentListeners.map(n => `${n}[name]`).join(`,`) - ).on(`change`, this._onChangeInput.bind(this)); + ).on(`change`, () => this._onChangeInput.bind(this)); /* Utility event listeners that apply diff --git a/module/sheets/Items/GenericItemSheet.mjs b/module/sheets/Items/GenericItemSheet.mjs index ef896a7..936bd6c 100644 --- a/module/sheets/Items/GenericItemSheet.mjs +++ b/module/sheets/Items/GenericItemSheet.mjs @@ -43,15 +43,6 @@ export class GenericItemSheet extends ItemSheet { if (!this.isEditable) return; console.debug(`.dungeon | Adding event listeners for Generic Item: ${this.id}`); - - /* - Custom element event listeners because Foundry doesn't listen to them by - default. - */ - html.find( - CONFIG.CACHE.componentListeners.map(n => `${n}[name]`).join(`,`) - ).on(`change`, this._onChangeInput.bind(this)); - html.find(`button[data-increment]`) .on(`click`, this._incrementValue.bind(this)); html.find(`button[data-decrement]`) diff --git a/module/sheets/Items/WeaponSheet.mjs b/module/sheets/Items/WeaponSheet.mjs deleted file mode 100644 index 2db0f23..0000000 --- a/module/sheets/Items/WeaponSheet.mjs +++ /dev/null @@ -1,116 +0,0 @@ -import { GenericContextMenu } from "../../utils/GenericContextMenu.mjs"; -import { DialogManager } from "../../utils/DialogManager.mjs"; -import { GenericItemSheet } from "./GenericItemSheet.mjs"; -import { localizer } from "../../utils/localizer.mjs"; - -export class WeaponSheet extends GenericItemSheet { - static get defaultOptions() { - let opts = foundry.utils.mergeObject( - super.defaultOptions, - { - template: `systems/dotdungeon/templates/items/weapon/v1/index.hbs`, - width: 300, - height: 340, - tabs: [ - { - group: `page`, - navSelector: `nav.page`, - contentSelector: `.page-content`, - initial: `general`, - }, - ], - } - ); - opts.classes.push(`dotdungeon`, `style-v3`); - return opts; - }; - - activateListeners(html) { - super.activateListeners(html); - - new GenericContextMenu(html, `.photo.panel`, [ - { - name: localizer(`dotdungeon.common.view-larger`), - callback: () => { - (new ImagePopout(this.item.img)).render(true); - }, - }, - { - name: localizer(`dotdungeon.common.edit`), - condition: () => this.isEditable, - callback: () => { - const fp = new FilePicker({ - callback: (path) => { - this.item.update({"img": path}); - }, - }); - fp.render(true); - }, - }, - { - name: localizer(`dotdungeon.common.reset`), - condition: () => this.isEditable, - callback: () => { - console.log(`.dungeon | Reset Item Image`) - }, - } - ]); - - if (!this.isEditable) return; - console.debug(`.dungeon | Adding event listeners for Weapon Item: ${this.item.id}`); - - html.find(`.create-ae`).on(`click`, async () => { - await this.item.createEmbeddedDocuments( - `ActiveEffect`, - [{name: localizer(`dotdungeon.default.name`, { document: `ActiveEffect`, type: `base` })}], - { renderSheet: true } - ); - }); - - new GenericContextMenu(html, `.effect.panel`, [ - { - name: localizer(`dotdungeon.common.edit`), - callback: async (html) => { - (await fromUuid(html.closest(`.effect`)[0].dataset.embeddedId))?.sheet.render(true); - }, - }, - { - name: localizer(`dotdungeon.common.delete`), - callback: async (html) => { - const target = html.closest(`.effect`)[0]; - const data = target.dataset; - const id = data.embeddedId; - const doc = await fromUuid(id); - DialogManager.createOrFocus( - `${doc.uuid}-delete`, - { - title: localizer(`dotdungeon.delete.ActiveEffect.title`, doc), - content: localizer(`dotdungeon.delete.ActiveEffect.content`, doc), - buttons: { - yes: { - label: localizer(`Yes`), - callback() { - doc.delete(); - }, - }, - no: { - label: localizer(`No`), - } - } - } - ); - }, - } - ]); - }; - - async getData() { - const ctx = await super.getData(); - - ctx.meta.showSettingsTab = ctx.isGM || this.item.isOwned; - ctx.meta.isEmbedded = this.item.isOwned; - ctx.meta.isEditable = this.isEditable; - - return ctx; - }; -}; diff --git a/new-pc-sheet.md b/new-pc-sheet.md new file mode 100644 index 0000000..f43ce4c --- /dev/null +++ b/new-pc-sheet.md @@ -0,0 +1,80 @@ +## Tabs: +- Main + - Stats + - Skills +- Inventory + - Player + - Containers + - Inventory (divided into category of items) + - This is the items that the player will have "on them" + - Storage (divided into category of items) + - This is all of the items that the players owns and has put into storage *somewhere* + - Transportation +- Combat + - Easy skill buttons: + - Melee + - Accuracy + - Weapons + - Sync / Respawns +- Info + - Account name + - PFP + - Group name + - Aspects + - Roles +- Spells + +====== + +## Requirements: + +Stats: + - Needs to list all 6 of the primary stats + - Needs to have a dropdown for to select a die + - Nice to have: disables dice that have been selected in other stats + - Needs to have a button to roll the stat (when a die is selected) + - Foundry v12: ActiveEffect - Die needs to be able to be affected by ActiveEffects + +Skills: + - Each of the 25 skills needs to be grouped under a header of what stat it's + associated with + - Each skill must have a dropdown to indicate training level (null, trained, + expert, locked) + - Every skill must have a button to roll the dice that is labelled with the + correct formula for that skill (or "Locked" if the skill is locked) + - ActiveEffect - Increase Modifier + - Foundry v12: ActiveEffect - Increase Training Level + +Combat: + - Two weapon slots for the equipped weapon(s) + - A single armor slot + - Quick-access to the Melee / Accuracy skills + +Inventory: + - Needs three sub-tabs: + - Player + - Storage + - Transportation + - Player Subtab: + - Needs to have a section for container items, and indicating how many slots + each one has. + - List all of the items that the player has with the "inventory" location + - Show the total number of items the player on their character and how many + total slots are available + - Needs some way to move items to a different storage area (embedded-only + item sheet field maybe) + - Storage Subtab: + - List all of the items that the player has marked as in-storage + - Transportation: + - This is currently just a placeholder tab, no functionality needed other + than existing + +Spells: + - Lists all spells on a page (sortable by: alphabetical, cost, etc.) + +Info: + - Needs a place to edit the actor's name + - Needs a place to edit the actor's image + - Needs a place to edit the group name (if enabled by the GM, or is the GM) + - Needs a place to see and manage all equipped aspects + - Needs a place to see and manage all equipped roles \ No newline at end of file diff --git a/styles/v3/components/range.scss b/styles/v3/components/range.scss deleted file mode 100644 index 15ea6d2..0000000 --- a/styles/v3/components/range.scss +++ /dev/null @@ -1,68 +0,0 @@ -/* -Disclaimer: This CSS is used by a custom web component and is scoped to JUST -the corresponding web component. Importing this into other files is forbidden -*/ - -@use "../mixins/material"; -@use "./common.scss"; - -.container { - @include material.elevate(4); - display: grid; - flex-direction: row; - gap: 4px; - border-radius: 4px; - grid-template-columns: minmax(0, 1fr); - grid-auto-columns: minmax(0, 1fr); - grid-template-rows: 1fr; - padding: 4px; - box-sizing: border-box; -} - -input { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; - - &:focus ~ .container { - @include material.elevate(8) - } -} - -.range-increment { - grid-row: 1; - - &:empty { - @include material.elevate(4); - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border-radius: 2px; - margin: 0; - cursor: pointer; - width: 1.25rem; - height: 1.25rem; - position: relative; - - &:hover { - @include material.elevate(8); - } - - &.filled::before { - content: ""; - background: var(--checkbox-checked); - border-radius: 3px; - $margin: 4px; - top: $margin; - bottom: $margin; - left: $margin; - right: $margin; - position: absolute; - } - } -} diff --git a/styles/v3/elements/panel.scss b/styles/v3/elements/panel.scss index 5780e47..de7cf9f 100644 --- a/styles/v3/elements/panel.scss +++ b/styles/v3/elements/panel.scss @@ -6,24 +6,16 @@ border-radius: 4px; padding: var(--gap); - &.space-between { - justify-content: space-between; - } - &--row { @extend .panel; display: flex; flex-direction: row; gap: var(--gap); align-items: center; - } - &--column { - @extend .panel; - display: flex; - flex-direction: column; - gap: var(--gap); - justify-content: center; + &.space-between { + justify-content: space-between; + } } } } diff --git a/styles/v3/index.scss b/styles/v3/index.scss index b71c20b..85ec4bf 100644 --- a/styles/v3/index.scss +++ b/styles/v3/index.scss @@ -19,9 +19,7 @@ /* Sheet Layouts */ @use "./layouts/datasheet.scss"; -@use "./layouts/items/common.scss"; @use "./layouts/items/untyped/v2.scss"; -@use "./layouts/items/weapon/v1.scss"; /* Sheet Options */ .dotdungeon.style-v3 { diff --git a/styles/v3/layouts/items/common.scss b/styles/v3/layouts/items/common.scss deleted file mode 100644 index 93c25dd..0000000 --- a/styles/v3/layouts/items/common.scss +++ /dev/null @@ -1,71 +0,0 @@ -@use "../../mixins/material"; -@use "../../mixins/utils"; - -.dotdungeon.style-v3 .item { - --gap: 8px; - - .nav-bar { - @include material.elevate(8, $base: var(--surface)); - position: absolute; - bottom: 0; - left: 0; - right: 6px; - - nav { - display: flex; - flex-direction: row; - gap: var(--gap); - padding: var(--gap); - } - } - - .page-content { - padding: calc(var(--gap) * 1); - padding-bottom: 60px; - height: 100%; - - > .tab { - height: 100%; - gap: var(--gap); - } - } - - @include utils.tab("general") { - display: grid; - --height: 50px; - grid-template-columns: var(--height) 1fr; - grid-template-rows: var(--height) 1fr; - - .description { - grid-column: 1 / -1; - } - } - - %flex-col { - display: flex; - flex-direction: column; - gap: 8px; - } - - @include utils.tab("effects") { - @extend %flex-col; - } - - @include utils.tab("settings") { - @extend %flex-col; - - .capacity-usage, .quantity-capacity, .combat-relevant { - display: flex; - align-items: center; - } - - .capacity { - &--calculated { - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; - } - } - } -} diff --git a/styles/v3/layouts/items/untyped/v2.scss b/styles/v3/layouts/items/untyped/v2.scss index 1b6097f..d94bf49 100644 --- a/styles/v3/layouts/items/untyped/v2.scss +++ b/styles/v3/layouts/items/untyped/v2.scss @@ -2,10 +2,53 @@ @use "../../../mixins/utils"; .dotdungeon.style-v3 .item--untyped { - @include utils.tab("details") { + --gap: 8px; + + .nav-bar { + @include material.elevate(8); + position: absolute; + bottom: 0; + left: 0; + right: 6px; + + nav { + display: flex; + flex-direction: row; + gap: var(--gap); + padding: var(--gap); + } + } + + .page-content { + padding: calc(var(--gap) * 1); + padding-bottom: 60px; + height: 100%; + + > .tab { + height: 100%; + gap: var(--gap); + } + } + + @include utils.tab("general") { + display: grid; + --height: 50px; + grid-template-columns: var(--height) 1fr; + grid-template-rows: var(--height) 1fr; + + .description { + grid-column: 1 / -1; + } + } + + %flex-col { display: flex; flex-direction: column; - gap: var(--gap); + gap: 8px; + } + + @include utils.tab("details") { + @extend %flex-col; .number { display: grid; @@ -13,4 +56,26 @@ align-items: center; } } + + @include utils.tab("effects") { + @extend %flex-col; + } + + @include utils.tab("settings") { + @extend %flex-col; + + .capacity-usage, .quantity-capacity, .combat-relevant { + display: flex; + align-items: center; + } + + .capacity { + &--calculated { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + } + } + } } diff --git a/styles/v3/layouts/items/weapon/v1.scss b/styles/v3/layouts/items/weapon/v1.scss deleted file mode 100644 index 82f8aaa..0000000 --- a/styles/v3/layouts/items/weapon/v1.scss +++ /dev/null @@ -1,13 +0,0 @@ -@use "../../../mixins/material"; -@use "../../../mixins/utils"; - -.dotdungeon.style-v3 .item--weapon-v1 { - @include utils.tab("details") { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - - .full-width { - grid-column: 1 / -1; - } - } -} diff --git a/styles/v3/mixins/_material.scss b/styles/v3/mixins/_material.scss index cdfd203..a869640 100644 --- a/styles/v3/mixins/_material.scss +++ b/styles/v3/mixins/_material.scss @@ -1,31 +1,12 @@ -@use "sass:map"; - -$elevations: ( - 0: 0%, - 1: 5%, - 2: 7%, - 3: 8%, - 4: 9%, - 6: 11%, - 8: 12%, - 12: 14%, - 16: 15%, - 24: 16%, -); - -@mixin elevate($height, $base: transparent) { - background-color: color-mix( - in lab, - #{$base}, - white map.get($elevations, $height) - ); +@mixin elevate($height) { + background-color: var(--elevation-#{$height}dp-bg); -webkit-box-shadow: 0px 0px #{$height * 1.75}px 0px rgba(0,0,0,0.75); -moz-box-shadow: 0px 0px #{$height * 1.75}px 0px rgba(0,0,0,0.75); box-shadow: 0px 0px #{$height * 1.75}px 0px rgba(0,0,0,0.75); } -@mixin undo($base: transparent) { - background-color: #{$base}; +@mixin undo { + background-color: transparent; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; diff --git a/system.json b/system.json index 6bbb1d8..5d3387c 100644 --- a/system.json +++ b/system.json @@ -7,9 +7,9 @@ "manifest": "https://github.com/Oliver-Akins/foundry.dungeon/releases/latest/download/system.json", "url": "https://github.com/Oliver-Akins/foundry.dungeon", "compatibility": { - "minimum": 12, - "verified": 12, - "maximum": 12 + "minimum": 11, + "verified": 11, + "maximum": 11 }, "authors": [ { diff --git a/templates/actors/char-sheet/v2/partials/stats.v2.pc.hbs b/templates/actors/char-sheet/v2/partials/stats.v2.pc.hbs index 59e61ca..6353120 100644 --- a/templates/actors/char-sheet/v2/partials/stats.v2.pc.hbs +++ b/templates/actors/char-sheet/v2/partials/stats.v2.pc.hbs @@ -17,9 +17,6 @@ data-roll-label="Stat Roll : {{stat.name}}" > Roll - {{#if stat.value}} - 1{{stat.value}} - {{/if}} {{#if stat.skills}} diff --git a/templates/items/untyped/v2/index.hbs b/templates/items/untyped/v2/index.hbs index bad820a..c8509ac 100644 --- a/templates/items/untyped/v2/index.hbs +++ b/templates/items/untyped/v2/index.hbs @@ -1,4 +1,4 @@ -
+
{{> dotdungeon.untyped.v2.general }} {{> dotdungeon.untyped.v2.details }} diff --git a/templates/items/weapon/v1/index.hbs b/templates/items/weapon/v1/index.hbs deleted file mode 100644 index 5863564..0000000 --- a/templates/items/weapon/v1/index.hbs +++ /dev/null @@ -1,21 +0,0 @@ - -
- {{> dotdungeon.untyped.v2.general }} - {{> dotdungeon.weapon.v1.details }} - {{#if meta.showSettingsTab}} - {{> dotdungeon.untyped.v2.settings }} - {{/if}} - {{> dotdungeon.untyped.v2.effects }} -
- - - diff --git a/templates/items/weapon/v1/tabs/details.v1.weapon.hbs b/templates/items/weapon/v1/tabs/details.v1.weapon.hbs deleted file mode 100644 index c60b42d..0000000 --- a/templates/items/weapon/v1/tabs/details.v1.weapon.hbs +++ /dev/null @@ -1,83 +0,0 @@ -
-
- {{#if meta.isEditable}} - - - {{else}} - Purchase Cost - {{dd-empty-state system.buy}} - {{/if}} -
-
- {{#if meta.isEditable}} - - - {{else}} - Usage Cost - {{dd-empty-state system.usage_cost}} - {{/if}} -
-
- Rarity - {{#if meta.isEditable}} - - {{else}} - {{dd-i18n (concat "dotdungeon.rarity." system.tier)}} - {{/if}} -
- {{#if meta.isEmbedded}} -
- Item Location - {{#if meta.isEditable}} - - {{else}} - {{dd-i18n (concat "dotdungeon.location." system.location)}} - {{/if}} -
-
- {{#if meta.isEditable}} - - - {{else}} - Quantity - {{system.quantity}} - {{/if}} -
- {{/if}} -