Merge f3aecc1ce8 into 2414fde703
This commit is contained in:
commit
5b12534126
44 changed files with 931 additions and 123 deletions
23
dev/hooks/getHeaderControlsActorSheetV2.mjs
Normal file
23
dev/hooks/getHeaderControlsActorSheetV2.mjs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
This hook exists to be able to change all of the actor-sheets to allow
|
||||
them to have dev-mode controls in their header that are useful for
|
||||
testing purposes. (This is particularly useful for testing embedded
|
||||
items that are only allowed to exist on specific actor types)
|
||||
*/
|
||||
Hooks.on(`getHeaderControlsActorSheetV2`, (app, controls) => {
|
||||
if (!game.settings.get(`ripcrypt`, `devMode`)) { return }
|
||||
|
||||
controls.push(
|
||||
{
|
||||
icon: `fa-solid fa-terminal`,
|
||||
label: `Embed New Item (DEV)`,
|
||||
action: `createItem`,
|
||||
},
|
||||
{
|
||||
label: `Make Global Reference`,
|
||||
onClick: () => {
|
||||
globalThis._doc = app.actor;
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Logger } from "../utils/Logger.mjs";
|
||||
import { Logger } from "../../module/utils/Logger.mjs";
|
||||
|
||||
const loaders = {
|
||||
svg(data) {
|
||||
3
dev/main.mjs
Normal file
3
dev/main.mjs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// Hooks
|
||||
import "./hooks/hotReload.mjs";
|
||||
import "./hooks/getHeaderControlsActorSheetV2.mjs";
|
||||
|
|
@ -74,7 +74,16 @@ export default [
|
|||
"@stylistic/space-infix-ops": `warn`,
|
||||
"@stylistic/eol-last": `warn`,
|
||||
"@stylistic/operator-linebreak": [`warn`, `before`],
|
||||
"@stylistic/indent": [`warn`, `tab`],
|
||||
"@stylistic/indent": [
|
||||
`warn`,
|
||||
`tab`,
|
||||
{
|
||||
SwitchCase: 1,
|
||||
ignoredNodes: [
|
||||
`> .superClass`,
|
||||
],
|
||||
},
|
||||
],
|
||||
"@stylistic/brace-style": [`warn`, `stroustrup`, { "allowSingleLine": true }],
|
||||
"@stylistic/quotes": [`warn`, `backtick`, { "avoidEscape": true }],
|
||||
"@stylistic/comma-dangle": [`warn`, { arrays: `always-multiline`, objects: `always-multiline`, imports: `always-multiline`, exports: `always-multiline`, functions: `always-multiline` }],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"include": [
|
||||
"module/**/*",
|
||||
"dev/**/*",
|
||||
"foundry/client/client.mjs",
|
||||
"foundry/client/global.d.mts",
|
||||
"foundry/common/primitives/global.d.mts"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"good": "Good",
|
||||
"shield": "Shield",
|
||||
"skill": "Skill",
|
||||
"trait": "Trait",
|
||||
"weapon": "Weapon"
|
||||
}
|
||||
},
|
||||
|
|
@ -173,6 +174,7 @@
|
|||
"guts-value-readonly": "The current amount of guts the character has",
|
||||
"guts-max-readonly": "The maximum amount of guts the character can have"
|
||||
},
|
||||
"edit-description": "Edit Description",
|
||||
"traits-placeholder": "New Trait...",
|
||||
"short-range": "Short @RipCrypt.common.range",
|
||||
"long-range": "Long @RipCrypt.common.range",
|
||||
|
|
@ -201,7 +203,8 @@
|
|||
"invalid-socket": "Invalid socket data received, this means a module or system bug is present.",
|
||||
"unknown-socket-event": "An unknown socket event was received: {event}",
|
||||
"no-active-gm": "No active @USER.GM is logged in, you must wait for a @USER.GM to be active before you can do that.",
|
||||
"malformed-socket-payload": "Socket event \"{event}\" received with malformed payload. Details: {details}"
|
||||
"malformed-socket-payload": "Socket event \"{event}\" received with malformed payload. Details: {details}",
|
||||
"invalid-parent-document": "Cannot create an item with type \"{itemType}\" on a parent document of type \"{parentType}\""
|
||||
},
|
||||
"warn": {
|
||||
"cannot-go-negative": "\"{name}\" is unable to be a negative number."
|
||||
|
|
|
|||
217
module/Apps/ActorSheets/BookGeistSheet.mjs
Normal file
217
module/Apps/ActorSheets/BookGeistSheet.mjs
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
import { filePath } from "../../consts.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { LaidOutAppMixin } from "../mixins/LaidOutAppMixin.mjs";
|
||||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
import { editItemFromElement, deleteItemFromElement } from "../utils.mjs";
|
||||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||
const { ContextMenu, TextEditor } = foundry.applications.ux;
|
||||
|
||||
export class BookGeistSheet extends
|
||||
LaidOutAppMixin(
|
||||
GenericAppMixin(
|
||||
HandlebarsApplicationMixin(
|
||||
ActorSheetV2,
|
||||
))) {
|
||||
// #region Options
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: [
|
||||
`ripcrypt--actor`,
|
||||
`BookGeistSheet`,
|
||||
],
|
||||
position: {
|
||||
width: `auto`,
|
||||
height: `auto`,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmite: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
layout: {
|
||||
root: true,
|
||||
template: filePath(`templates/Apps/BookGeistSheet/layout.hbs`),
|
||||
},
|
||||
image: { template: filePath(`templates/Apps/BookGeistSheet/image.hbs`) },
|
||||
header: { template: filePath(`templates/Apps/BookGeistSheet/header.hbs`) },
|
||||
stats: { template: filePath(`templates/Apps/BookGeistSheet/stats.hbs`) },
|
||||
items: { template: filePath(`templates/Apps/BookGeistSheet/items.hbs`) },
|
||||
};
|
||||
// #endregion Options
|
||||
|
||||
// #region Lifecycle
|
||||
async _onRender() {
|
||||
await super._onRender();
|
||||
|
||||
new ContextMenu.implementation(
|
||||
this.element,
|
||||
`[data-ctx-menu="item"]`,
|
||||
[
|
||||
{
|
||||
name: localizer(`RipCrypt.common.edit`),
|
||||
condition: (el) => {
|
||||
const itemId = el.dataset.itemId;
|
||||
return itemId !== ``;
|
||||
},
|
||||
callback: editItemFromElement,
|
||||
},
|
||||
{
|
||||
name: localizer(`RipCrypt.common.delete`),
|
||||
condition: (el) => {
|
||||
const itemId = el.dataset.itemId;
|
||||
return itemId !== ``;
|
||||
},
|
||||
callback: deleteItemFromElement,
|
||||
},
|
||||
],
|
||||
{ jQuery: false, fixed: true },
|
||||
);
|
||||
};
|
||||
// #endregion Lifecycle
|
||||
|
||||
// #region Data Prep
|
||||
async _preparePartContext(partID, ctx) {
|
||||
|
||||
switch (partID) {
|
||||
case `layout`: await this._prepareLayoutContext(ctx); break;
|
||||
case `image`: await this._prepareImageContext(ctx); break;
|
||||
case `header`: await this._prepareHeaderContext(ctx); break;
|
||||
case `stats`: await this._prepareStatsContext(ctx); break;
|
||||
case `items`: await this._prepareItemsContext(ctx); break;
|
||||
};
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
async _prepareLayoutContext(ctx) {
|
||||
ctx.imageVisible = true;
|
||||
};
|
||||
|
||||
async _prepareImageContext(ctx) {
|
||||
ctx.url = this.actor.img;
|
||||
};
|
||||
|
||||
async _prepareHeaderContext(ctx) {
|
||||
ctx.name = this.actor.name;
|
||||
ctx.rank = this.actor.system.level.rank;
|
||||
ctx.ranks = Object.values(gameTerms.Rank)
|
||||
.map((value, index) => ({
|
||||
value,
|
||||
label: index
|
||||
}));
|
||||
ctx.description = await TextEditor.implementation.enrichHTML(this.actor.system.description);
|
||||
};
|
||||
|
||||
async _prepareStatsContext(ctx) {
|
||||
const system = this.actor.system;
|
||||
|
||||
const fate = system.fate;
|
||||
if (fate) {
|
||||
ctx.path = {
|
||||
full: localizer(`RipCrypt.common.ordinals.${fate}.full`),
|
||||
abbv: localizer(`RipCrypt.common.ordinals.${fate}.abbv`),
|
||||
};
|
||||
}
|
||||
else {
|
||||
ctx.path = {
|
||||
full: null,
|
||||
abbv: localizer(`RipCrypt.common.empty`),
|
||||
};
|
||||
};
|
||||
|
||||
Object.assign(ctx, system.ability);
|
||||
ctx.guts = system.guts;
|
||||
ctx.speed = system.speed;
|
||||
};
|
||||
|
||||
async _prepareItemsContext(ctx) {
|
||||
const armours = this.actor.system.equippedArmour;
|
||||
const shield = this.actor.system.equippedShield;
|
||||
|
||||
let defenses = [];
|
||||
for (const [location, armour] of Object.entries(armours)) {
|
||||
if (!armour) { continue }
|
||||
const defense = {
|
||||
name: localizer(`RipCrypt.common.anatomy.${location}`),
|
||||
tooltip: null,
|
||||
protection: 0,
|
||||
shielded: false,
|
||||
};
|
||||
if (armour) {
|
||||
defense.armourUUID = armour.uuid;
|
||||
defense.tooltip = armour.name,
|
||||
defense.protection = armour.system.protection;
|
||||
}
|
||||
if (shield?.system.location.has(location)) {
|
||||
defense.shieldUUID = shield.uuid;
|
||||
defense.shielded = true;
|
||||
defense.protection += shield.system.protection;
|
||||
};
|
||||
if (defense.protection > 0) {
|
||||
defenses.push(defense);
|
||||
};
|
||||
};
|
||||
|
||||
ctx.defenses = defenses
|
||||
ctx.traits = []; // Array<{name: string}>
|
||||
|
||||
for (const item of this.actor.items) {
|
||||
switch (item.type) {
|
||||
case `weapon`: {
|
||||
if (!item.system.equipped) { continue };
|
||||
ctx.attacks ??= [];
|
||||
ctx.attacks.push(this._prepareWeapon(item));
|
||||
break;
|
||||
};
|
||||
case `craft`: {
|
||||
ctx.crafts ??= [];
|
||||
ctx.crafts.push(this._prepareCraft(item));
|
||||
break;
|
||||
};
|
||||
case `trait`: {
|
||||
ctx.traits.push(this._prepareTrait(item));
|
||||
break;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
_prepareWeapon(weapon) {
|
||||
const hasShortRange = weapon.system.range.short != null;
|
||||
const hasLongRange = weapon.system.range.long != null;
|
||||
const isRanged = hasShortRange || hasLongRange;
|
||||
return {
|
||||
uuid: weapon.uuid,
|
||||
name: weapon.name,
|
||||
damage: weapon.system.damage,
|
||||
isRanged,
|
||||
range: weapon.system.range,
|
||||
};
|
||||
};
|
||||
|
||||
_prepareCraft(craft) {
|
||||
return {
|
||||
uuid: craft.uuid,
|
||||
name: craft.name,
|
||||
};
|
||||
};
|
||||
|
||||
_prepareTrait(trait) {
|
||||
return {
|
||||
uuid: trait.uuid,
|
||||
name: trait.name,
|
||||
description: trait.system.description,
|
||||
};
|
||||
};
|
||||
// #endregion Data Prep
|
||||
|
||||
// #region Actions
|
||||
// #endregion Actions
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { CraftCardV1 } from "./CraftCardV1.mjs";
|
||||
import { filePath } from "../../consts.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { SkillsCardV1 } from "./SkillsCardV1.mjs";
|
||||
import { StatsCardV1 } from "./StatsCardV1.mjs";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
|
||||
import { documentSorter, filePath } from "../../consts.mjs";
|
||||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
import { Logger } from "../../utils/Logger.mjs";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
|
|||
import { documentSorter, filePath } from "../../consts.mjs";
|
||||
import { AmmoTracker } from "../popovers/AmmoTracker.mjs";
|
||||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { ItemFlags } from "../../flags/item.mjs";
|
||||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
import { Logger } from "../../utils/Logger.mjs";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { deleteItemFromElement, editItemFromElement } from "../utils.mjs";
|
|||
import { DelveDiceHUD } from "../DelveDiceHUD.mjs";
|
||||
import { filePath } from "../../consts.mjs";
|
||||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
import { Logger } from "../../utils/Logger.mjs";
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ export class DelveDiceHUD extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||
};
|
||||
};
|
||||
|
||||
Logger.log(`${partId} Context`, ctx);
|
||||
return ctx;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { filePath } from "../consts.mjs";
|
||||
import { GenericAppMixin } from "./GenericApp.mjs";
|
||||
import { GenericAppMixin } from "./mixins/GenericApp.mjs";
|
||||
import { localizer } from "../utils/Localizer.mjs";
|
||||
import { Logger } from "../utils/Logger.mjs";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { filePath } from "../../consts.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
import { Logger } from "../../utils/Logger.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { filePath } from "../../consts.mjs";
|
||||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
import { GenericAppMixin } from "../GenericApp.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||
|
|
|
|||
53
module/Apps/ItemSheets/TraitSheet.mjs
Normal file
53
module/Apps/ItemSheets/TraitSheet.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { filePath } from "../../consts.mjs";
|
||||
import { GenericAppMixin } from "../mixins/GenericApp.mjs";
|
||||
|
||||
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||
|
||||
export class TraitSheet extends GenericAppMixin(HandlebarsApplicationMixin(ItemSheetV2)) {
|
||||
// #region Options
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: [
|
||||
`ripcrypt--item`,
|
||||
`TraitSheet`,
|
||||
],
|
||||
position: {
|
||||
width: `auto`,
|
||||
height: `auto`,
|
||||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
},
|
||||
form: {
|
||||
submitOnChange: true,
|
||||
closeOnSubmit: false,
|
||||
},
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
content: {
|
||||
template: filePath(`templates/Apps/TraitSheet/content.hbs`),
|
||||
root: true,
|
||||
},
|
||||
};
|
||||
// #endregion Options
|
||||
|
||||
// #region Data Prep
|
||||
async _prepareContext() {
|
||||
const TextEditor = foundry.applications.ux.TextEditor.implementation;
|
||||
const ctx = {
|
||||
meta: {
|
||||
idp: this.id,
|
||||
},
|
||||
item: this.document,
|
||||
enriched: {
|
||||
system: {
|
||||
description: await TextEditor.enrichHTML(this.document.system.description),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return ctx;
|
||||
};
|
||||
// #endregion Data Prep
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { createItemFromElement, deleteItemFromElement, editItemFromElement, updateForeignDocumentFromEvent } from "./utils.mjs";
|
||||
import { DicePool } from "./DicePool.mjs";
|
||||
import { RichEditor } from "./RichEditor.mjs";
|
||||
import { toBoolean } from "../consts.mjs";
|
||||
import { createItemFromElement, deleteItemFromElement, editItemFromElement, updateForeignDocumentFromEvent } from "../utils.mjs";
|
||||
import { DicePool } from "../DicePool.mjs";
|
||||
import { RichEditor } from "../RichEditor.mjs";
|
||||
import { toBoolean } from "../../consts.mjs";
|
||||
|
||||
/**
|
||||
* A mixin that takes the class from HandlebarsApplicationMixin and combines it
|
||||
|
|
@ -37,6 +37,8 @@ export function GenericAppMixin(HandlebarsApp) {
|
|||
_popoverManagers = new Map();
|
||||
/** @type {Map<number, string>} */
|
||||
_hookIDs = new Map();
|
||||
/** @type {string | null} */
|
||||
#focus = null;
|
||||
// #endregion
|
||||
|
||||
// #region Lifecycle
|
||||
|
|
@ -53,6 +55,26 @@ export function GenericAppMixin(HandlebarsApp) {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
* This method overrides Foundry's default behaviour for caching the focused
|
||||
* element so that it actually works when the application has a root partial
|
||||
*/
|
||||
async _preRender(...args) {
|
||||
if (this.rendered) {
|
||||
const target = this.element.querySelector(`:focus`);
|
||||
if (target) {
|
||||
if (target.id) {
|
||||
this.#focus = `#${CSS.escape(target.id)}`;
|
||||
}
|
||||
else if (target.name) {
|
||||
this.#focus = `${target.tagName}[name="${target.name}"]`;
|
||||
};
|
||||
};
|
||||
};
|
||||
return super._preRender(...args);
|
||||
};
|
||||
|
||||
/** @override */
|
||||
async _onRender(...args) {
|
||||
await super._onRender(...args);
|
||||
|
|
@ -83,19 +105,30 @@ export function GenericAppMixin(HandlebarsApp) {
|
|||
});
|
||||
};
|
||||
|
||||
async _preparePartContext(partId, ctx, opts) {
|
||||
ctx = await super._preparePartContext(partId, ctx, opts);
|
||||
delete ctx.document;
|
||||
delete ctx.fields;
|
||||
/**
|
||||
* @override
|
||||
* This method overrides Foundry's default behaviour for caching the focused
|
||||
* element so that it actually works when the application has a root partial
|
||||
*/
|
||||
async _postRender(...args) {
|
||||
if (this.rendered) {
|
||||
const target = this.element.querySelector(this.#focus);
|
||||
target?.focus();
|
||||
};
|
||||
this.#focus = null;
|
||||
return super._postRender(...args);
|
||||
};
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const ctx = {};
|
||||
|
||||
ctx.meta ??= {};
|
||||
ctx.meta.idp = this.document?.uuid ?? this.id;
|
||||
ctx.meta.idp = this.id;
|
||||
if (this.document) {
|
||||
ctx.meta.limited = this.document.limited;
|
||||
ctx.meta.editable = this.isEditable || game.user.isGM;
|
||||
ctx.meta.embedded = this.document.isEmbedded;
|
||||
};
|
||||
delete ctx.editable;
|
||||
|
||||
return ctx;
|
||||
};
|
||||
56
module/Apps/mixins/LaidOutAppMixin.mjs
Normal file
56
module/Apps/mixins/LaidOutAppMixin.mjs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* This mixin makes it so that we can provide a specific layout template without
|
||||
* needing to reference each of the inner areas via a partial embedded in the root,
|
||||
* enabling partial re-renders for parts of the sheet without losing advanced
|
||||
* layout capabilities.
|
||||
*
|
||||
* @param {ReturnType<HandlebarsApp>} HandlebarsApp The mixin'd class from HAM to further mix
|
||||
*/
|
||||
export function LaidOutAppMixin(HandlebarsApp) {
|
||||
class LaidOutApp extends HandlebarsApp {
|
||||
#partDescriptors;
|
||||
|
||||
/**
|
||||
* This caches the part descriptors into this class of the heirarchy, because
|
||||
* Foundry doesn't expose the partDescriptors from the HAM directly, so we
|
||||
* inject a heirarchy call so that we can nab the pointer that Foundry has
|
||||
* in the HAM so that we can also read/write it from this class.
|
||||
*/
|
||||
_configureRenderParts(options) {
|
||||
const parts = super._configureRenderParts(options);
|
||||
this.#partDescriptors = parts;
|
||||
return parts;
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
* This is essentially Foundry's HandlebarsApplicationMixin implementation,
|
||||
* however if an existing part for non-root elements don't get concatenated
|
||||
* into the DOM.
|
||||
*/
|
||||
_replaceHTML(result, content, options) {
|
||||
const partInfo = this.#partDescriptors;
|
||||
for ( const [partId, htmlElement] of Object.entries(result) ) {
|
||||
const part = partInfo[partId];
|
||||
const priorElement = part.root ? content : content.querySelector(`[data-application-part="${partId}"]`);
|
||||
const state = {};
|
||||
if ( priorElement ) {
|
||||
this._preSyncPartState(partId, htmlElement, priorElement, state);
|
||||
if ( part.root ) {
|
||||
priorElement.replaceChildren(...htmlElement.children);
|
||||
}
|
||||
else {
|
||||
priorElement.replaceWith(htmlElement);
|
||||
}
|
||||
this._syncPartState(partId, htmlElement, priorElement, state);
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
this._attachPartListeners(partId, htmlElement, options);
|
||||
this.parts[partId] = htmlElement;
|
||||
}
|
||||
};
|
||||
};
|
||||
return LaidOutApp;
|
||||
};
|
||||
|
|
@ -1,3 +1,18 @@
|
|||
import { EntityData } from "./Entity.mjs";
|
||||
|
||||
export class GeistData extends EntityData {};
|
||||
const { fields } = foundry.data;
|
||||
|
||||
export class GeistData extends EntityData {
|
||||
static defineSchema() {
|
||||
const schema = super.defineSchema();
|
||||
|
||||
schema.description = new fields.HTMLField({
|
||||
blank: true,
|
||||
nullable: true,
|
||||
trim: true,
|
||||
initial: null,
|
||||
});
|
||||
|
||||
return schema;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,19 +2,6 @@ import { CommonItemData } from "./Common.mjs";
|
|||
import { gameTerms } from "../../gameTerms.mjs";
|
||||
|
||||
export class AmmoData extends CommonItemData {
|
||||
// MARK: Base Data
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
};
|
||||
|
||||
// MARK: Derived Data
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
};
|
||||
|
||||
// #region Getters
|
||||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
getFormFields(_ctx) {
|
||||
const fields = [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { gameTerms } from "../../gameTerms.mjs";
|
|||
const { fields } = foundry.data;
|
||||
|
||||
export class CommonItemData extends foundry.abstract.TypeDataModel {
|
||||
// MARK: Schema
|
||||
// #region Schema
|
||||
static defineSchema() {
|
||||
return {
|
||||
quantity: requiredInteger({ min: 0, initial: 1 }),
|
||||
|
|
@ -21,14 +21,5 @@ export class CommonItemData extends foundry.abstract.TypeDataModel {
|
|||
}),
|
||||
};
|
||||
};
|
||||
|
||||
// MARK: Base Data
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
};
|
||||
|
||||
// MARK: Derived Data
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
};
|
||||
// #endregion Schema
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,19 +21,6 @@ export class CraftData extends SkillData {
|
|||
return schema;
|
||||
};
|
||||
|
||||
// MARK: Base Data
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
};
|
||||
|
||||
// MARK: Derived Data
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
};
|
||||
|
||||
// #region Getters
|
||||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
async getFormFields(_ctx) {
|
||||
const fields = [
|
||||
|
|
|
|||
|
|
@ -17,19 +17,6 @@ export class GoodData extends CommonItemData {
|
|||
return schema;
|
||||
};
|
||||
|
||||
// MARK: Base Data
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
};
|
||||
|
||||
// MARK: Derived Data
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
};
|
||||
|
||||
// #region Getters
|
||||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
async getFormFields(_ctx) {
|
||||
const fields = [
|
||||
|
|
|
|||
|
|
@ -34,19 +34,6 @@ export class SkillData extends foundry.abstract.TypeDataModel {
|
|||
return schema;
|
||||
};
|
||||
|
||||
// MARK: Base Data
|
||||
prepareBaseData() {
|
||||
super.prepareBaseData();
|
||||
};
|
||||
|
||||
// MARK: Derived Data
|
||||
prepareDerivedData() {
|
||||
super.prepareDerivedData();
|
||||
};
|
||||
|
||||
// #region Getters
|
||||
// #endregion
|
||||
|
||||
// #region Sheet Data
|
||||
async getFormFields(_ctx) {
|
||||
const fields = [
|
||||
|
|
|
|||
25
module/data/Item/Trait.mjs
Normal file
25
module/data/Item/Trait.mjs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { localizer } from "../../utils/Localizer.mjs";
|
||||
|
||||
const { fields } = foundry.data;
|
||||
|
||||
export class TraitData extends foundry.abstract.TypeDataModel {
|
||||
// #region Schema
|
||||
static defineSchema() {
|
||||
return {
|
||||
description: new fields.HTMLField({ blank: true, nullable: false, trim: true }),
|
||||
};
|
||||
};
|
||||
// #endregion Schema
|
||||
|
||||
// #region Lifecycle
|
||||
async _preCreate() {
|
||||
if (this.parent.isEmbedded && this.parent.parent.type !== `geist`) {
|
||||
ui.notifications.error(localizer(
|
||||
`RipCrypt.notifs.error.invalid-parent-document`,
|
||||
{ itemType: `trait`, parentType: this.parent.parent.type },
|
||||
));
|
||||
return false;
|
||||
};
|
||||
};
|
||||
// #endregion
|
||||
};
|
||||
|
|
@ -49,10 +49,13 @@ export class WeaponData extends CommonItemData {
|
|||
async _preCreate(item, options) {
|
||||
const showEquipPrompt = options.showEquipPrompt ?? true;
|
||||
if (showEquipPrompt && this.parent.isEmbedded && this._canEquip()) {
|
||||
const shouldEquip = await DialogV2.confirm({
|
||||
let shouldEquip = this.parent.parent.type === `geist`;
|
||||
|
||||
shouldEquip ||= await DialogV2.confirm({
|
||||
window: { title: `Equip Item?` },
|
||||
content: `Do you want to equip ${item.name}?`,
|
||||
});
|
||||
|
||||
if (shouldEquip) {
|
||||
this.updateSource({ "equipped": true });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ export default {
|
|||
"rc-options": options,
|
||||
|
||||
// #region Simple
|
||||
"rc-ifOut": (v) => (v || ``),
|
||||
"rc-empty-state": (v) => v ?? localizer(`RipCrypt.common.empty`),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Applications
|
||||
import { AllItemSheetV1 } from "../Apps/ItemSheets/AllItemSheetV1.mjs";
|
||||
import { ArmourSheet } from "../Apps/ItemSheets/ArmourSheet.mjs";
|
||||
import { BookGeistSheet } from "../Apps/ActorSheets/BookGeistSheet.mjs";
|
||||
import { CombinedHeroSheet } from "../Apps/ActorSheets/CombinedHeroSheet.mjs";
|
||||
import { CraftCardV1 } from "../Apps/ActorSheets/CraftCardV1.mjs";
|
||||
import { DelveDiceHUD } from "../Apps/DelveDiceHUD.mjs";
|
||||
|
|
@ -17,6 +18,7 @@ import { GoodData } from "../data/Item/Good.mjs";
|
|||
import { HeroData } from "../data/Actor/Hero.mjs";
|
||||
import { ShieldData } from "../data/Item/Shield.mjs";
|
||||
import { SkillData } from "../data/Item/Skill.mjs";
|
||||
import { TraitData } from "../data/Item/Trait.mjs";
|
||||
import { WeaponData } from "../data/Item/Weapon.mjs";
|
||||
|
||||
// Class Overrides
|
||||
|
|
@ -37,6 +39,7 @@ import { registerMetaSettings } from "../settings/metaSettings.mjs";
|
|||
import { registerSockets } from "../sockets/_index.mjs";
|
||||
import { registerUserSettings } from "../settings/userSettings.mjs";
|
||||
import { registerWorldSettings } from "../settings/worldSettings.mjs";
|
||||
import { TraitSheet } from "../Apps/ItemSheets/TraitSheet.mjs";
|
||||
|
||||
const { Items, Actors } = foundry.documents.collections;
|
||||
|
||||
|
|
@ -62,6 +65,7 @@ Hooks.once(`init`, () => {
|
|||
CONFIG.Item.dataModels.good = GoodData;
|
||||
CONFIG.Item.dataModels.shield = ShieldData;
|
||||
CONFIG.Item.dataModels.skill = SkillData;
|
||||
CONFIG.Item.dataModels.trait = TraitData;
|
||||
CONFIG.Item.dataModels.weapon = WeaponData;
|
||||
// #endregion
|
||||
|
||||
|
|
@ -87,22 +91,21 @@ Hooks.once(`init`, () => {
|
|||
label: `RipCrypt.sheet-names.StatsCardV1`,
|
||||
themes: StatsCardV1.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, StatsCardV1, {
|
||||
makeDefault: true,
|
||||
types: [`geist`],
|
||||
label: `RipCrypt.sheet-names.StatsCardV1`,
|
||||
themes: StatsCardV1.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, SkillsCardV1, {
|
||||
types: [`hero`, `geist`],
|
||||
types: [`hero`],
|
||||
label: `RipCrypt.sheet-names.SkillsCardV1`,
|
||||
themes: SkillsCardV1.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, CraftCardV1, {
|
||||
types: [`hero`, `geist`],
|
||||
types: [`hero`],
|
||||
label: `RipCrypt.sheet-names.CraftCardV1`,
|
||||
themes: CraftCardV1.themes,
|
||||
});
|
||||
Actors.registerSheet(game.system.id, BookGeistSheet, {
|
||||
types: [`geist`],
|
||||
label: `RipCrypt.sheet-names.BookGeistSheet`,
|
||||
themes: BookGeistSheet.themes,
|
||||
});
|
||||
// #endregion
|
||||
|
||||
// #region Items
|
||||
|
|
@ -118,8 +121,15 @@ Hooks.once(`init`, () => {
|
|||
label: `RipCrypt.sheet-names.ArmourSheet`,
|
||||
themes: ArmourSheet.themes,
|
||||
});
|
||||
Items.registerSheet(game.system.id, TraitSheet, {
|
||||
makeDefault: true,
|
||||
types: [`trait`],
|
||||
label: `RipCrypt.sheet-names.TraitSheet`,
|
||||
themes: TraitSheet.themes,
|
||||
});
|
||||
|
||||
Items.unregisterSheet(game.system.id, AllItemSheetV1, {
|
||||
types: [`armour`, `shield`],
|
||||
types: [`armour`, `shield`, `trait`],
|
||||
});
|
||||
// #endregion
|
||||
// #endregion
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// Hooks
|
||||
import "./hooks/init.mjs";
|
||||
import "./hooks/ready.mjs";
|
||||
import "./hooks/hotReload.mjs";
|
||||
|
||||
// Global API
|
||||
import "./api.mjs";
|
||||
|
|
|
|||
34
scripts/prepareManifest.mjs
Normal file
34
scripts/prepareManifest.mjs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
The intent of this script is to do all of the modifications of the
|
||||
manifest file that we need to do in order to release the system. This
|
||||
can include removing dev-only fields/attributes that end users will
|
||||
never, and should never, care about nor need.
|
||||
*/
|
||||
import { readFile, writeFile } from "fs/promises";
|
||||
|
||||
const MANIFEST_PATH = `system.json`;
|
||||
|
||||
let manifest;
|
||||
try {
|
||||
manifest = JSON.parse(await readFile(MANIFEST_PATH, `utf-8`));
|
||||
} catch {
|
||||
console.error(`Failed to parse manifest file.`);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
|
||||
// Filter out dev-only resources
|
||||
if (manifest.esmodules) {
|
||||
manifest.esmodules = manifest.esmodules.filter(
|
||||
filepath => !filepath.startsWith(`dev/`)
|
||||
);
|
||||
};
|
||||
|
||||
// Remove dev flags
|
||||
delete manifest.flags?.hotReload;
|
||||
|
||||
if (Object.keys(manifest.flags).length === 0) {
|
||||
delete manifest.flags;
|
||||
};
|
||||
|
||||
await writeFile(MANIFEST_PATH, JSON.stringify(manifest, undefined, `\t`));
|
||||
22
system.json
22
system.json
|
|
@ -12,7 +12,8 @@
|
|||
{ "name": "Oliver" }
|
||||
],
|
||||
"esmodules": [
|
||||
"module/main.mjs"
|
||||
"module/main.mjs",
|
||||
"dev/main.mjs"
|
||||
],
|
||||
"styles": [
|
||||
{
|
||||
|
|
@ -36,7 +37,7 @@
|
|||
"flags": {
|
||||
"hotReload": {
|
||||
"extensions": ["css", "hbs", "json", "mjs", "svg"],
|
||||
"paths": ["assets", "templates", "langs", "module"]
|
||||
"paths": ["assets", "templates", "langs", "module", "dev"]
|
||||
}
|
||||
},
|
||||
"documentTypes": {
|
||||
|
|
@ -48,9 +49,22 @@
|
|||
"ammo": {},
|
||||
"armour": {},
|
||||
"craft": {},
|
||||
"good": {},
|
||||
"good": {
|
||||
"htmlFields": [
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"shield": {},
|
||||
"skill": {},
|
||||
"skill": {
|
||||
"htmlFields": [
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"trait": {
|
||||
"htmlFields": [
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"weapon": {}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
24
templates/Apps/BookGeistSheet/header.hbs
Normal file
24
templates/Apps/BookGeistSheet/header.hbs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<div>
|
||||
<div class="overview">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="{{name}}"
|
||||
>
|
||||
<div class="grow"></div>
|
||||
<label for="{{meta-idp}}-rank">
|
||||
Rank
|
||||
</label>
|
||||
<select
|
||||
id="{{meta.idp}}-rank"
|
||||
name="system.level.rank"
|
||||
>
|
||||
{{rc-options rank ranks}}
|
||||
</select>
|
||||
</div>
|
||||
{{#if description}}
|
||||
<div class="description">
|
||||
{{{description}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
8
templates/Apps/BookGeistSheet/image.hbs
Normal file
8
templates/Apps/BookGeistSheet/image.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<div class="img-wrapper">
|
||||
<img
|
||||
src="{{url}}"
|
||||
alt=""
|
||||
data-action="editImage"
|
||||
data-edit="img"
|
||||
>
|
||||
</div>
|
||||
78
templates/Apps/BookGeistSheet/items.hbs
Normal file
78
templates/Apps/BookGeistSheet/items.hbs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<div class="items">
|
||||
{{#if attacks}}
|
||||
<div>Attacks</div>
|
||||
<div>
|
||||
{{#each attacks as |attack|}}
|
||||
<div
|
||||
class="attack"
|
||||
data-ctx-menu="item"
|
||||
data-item-id="{{attack.uuid}}"
|
||||
>
|
||||
{{attack.name}}
|
||||
{{attack.damage}}
|
||||
{{#if attack.isRanged}}
|
||||
<span class="range">
|
||||
({{attack.range.short}} / {{attack.range.long}})
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if crafts}}
|
||||
<div>Craft</div>
|
||||
<div>
|
||||
{{#each crafts as |craft|}}
|
||||
<div
|
||||
class="craft"
|
||||
data-ctx-menu="item"
|
||||
data-item-id="{{craft.uuid}}"
|
||||
>
|
||||
{{craft.name}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>Defense</div>
|
||||
<ul>
|
||||
{{#each defenses as |defense|}}
|
||||
<li
|
||||
class="defense"
|
||||
data-tooltip="{{defense.tooltip}}"
|
||||
data-ctx-menu="item"
|
||||
data-item-id="{{defense.armourUUID}}"
|
||||
>
|
||||
{{defense.name}} ({{defense.protection}}{{#if defense.shielded}},
|
||||
<rc-icon
|
||||
name="icons/shield/solid.v1"
|
||||
var:size="14px"
|
||||
var:fill="currentColor"
|
||||
data-ctx-menu="item"
|
||||
data-item-id="{{defense.shieldUUID}}"
|
||||
/>
|
||||
{{/if}})
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
<div>Traits</div>
|
||||
<ul class="traits">
|
||||
{{#each traits as |trait|}}
|
||||
<li
|
||||
class="trait"
|
||||
data-ctx-menu="item"
|
||||
data-item-id="{{trait.uuid}}"
|
||||
>
|
||||
{{trait.name}}
|
||||
{{#if trait.description}}
|
||||
<rc-icon
|
||||
var:fill="currentColor"
|
||||
name="icons/info-circle"
|
||||
data-tooltip="{{trait.description}}"
|
||||
/>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{else}}
|
||||
None
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
10
templates/Apps/BookGeistSheet/layout.hbs
Normal file
10
templates/Apps/BookGeistSheet/layout.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<div>
|
||||
{{#if imageVisible}}
|
||||
<div data-application-part="image"></div>
|
||||
{{/if}}
|
||||
<div class="info">
|
||||
<div data-application-part="header"></div>
|
||||
<div data-application-part="stats"></div>
|
||||
<div data-application-part="items"></div>
|
||||
</div>
|
||||
</div>
|
||||
97
templates/Apps/BookGeistSheet/stats.hbs
Normal file
97
templates/Apps/BookGeistSheet/stats.hbs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="alt">Path</td>
|
||||
<td>Grit</td>
|
||||
<td>Gait</td>
|
||||
<td>Grip</td>
|
||||
<td>Glim</td>
|
||||
<td class="alt">Guts</td>
|
||||
<td class="alt">Move</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="alt"
|
||||
data-tooltip="{{path.full}}"
|
||||
>
|
||||
{{path.abbv}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if meta.editable}}
|
||||
<input
|
||||
type="number"
|
||||
id="{{meta.idp}}-grit"
|
||||
value="{{grit}}"
|
||||
name="system.ability.grit"
|
||||
>
|
||||
{{else if meta.limited}}
|
||||
???
|
||||
{{else}}
|
||||
{{grit}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if meta.editable}}
|
||||
<input
|
||||
type="number"
|
||||
id="{{meta.idp}}-gait"
|
||||
value="{{gait}}"
|
||||
name="system.ability.gait"
|
||||
>
|
||||
{{else if meta.limited}}
|
||||
???
|
||||
{{else}}
|
||||
{{gait}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if meta.editable}}
|
||||
<input
|
||||
type="number"
|
||||
id="{{meta.idp}}-grip"
|
||||
value="{{grip}}"
|
||||
name="system.ability.grip"
|
||||
>
|
||||
{{else if meta.limited}}
|
||||
???
|
||||
{{else}}
|
||||
{{grip}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>
|
||||
{{#if meta.editable}}
|
||||
<input
|
||||
type="number"
|
||||
id="{{meta.idp}}-glim"
|
||||
value="{{glim}}"
|
||||
name="system.ability.glim"
|
||||
>
|
||||
{{else if meta.limited}}
|
||||
???
|
||||
{{else}}
|
||||
{{glim}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="alt">
|
||||
{{#if meta.editable}}
|
||||
<input
|
||||
type="number"
|
||||
id="{{meta.idp}}-guts-value"
|
||||
value="{{guts.value}}"
|
||||
name="system.guts.value"
|
||||
>
|
||||
/ {{guts.max}}
|
||||
{{else if meta.limited}}
|
||||
??/??
|
||||
{{else}}
|
||||
{{guts.value}}/{{guts.max}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="alt">{{speed.move}} / {{speed.run}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
101
templates/Apps/BookGeistSheet/style.css
Normal file
101
templates/Apps/BookGeistSheet/style.css
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
.BookGeistSheet {
|
||||
> .window-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
color: var(--base-text);
|
||||
background: var(--base-background);
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.img-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.overview {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
|
||||
input {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
td {
|
||||
border: 1px solid var(--accent-1);
|
||||
text-align: center;
|
||||
|
||||
input {
|
||||
width: 30px;
|
||||
background: unset;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
thead td {
|
||||
font-weight: bold;
|
||||
border-top-width: 0;
|
||||
|
||||
&:first-of-type, &:last-of-type {
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
td:first-of-type, td:last-of-type {
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
&:last-of-type td {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.alt {
|
||||
background-color: var(--alt-row-background);
|
||||
}
|
||||
}
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 4fr);
|
||||
grid-template-rows: repeat(3, auto);
|
||||
gap: 2px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 4px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background-color: var(--accent-2);
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
templates/Apps/TraitSheet/content.hbs
Normal file
21
templates/Apps/TraitSheet/content.hbs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<div>
|
||||
<input
|
||||
type="text"
|
||||
class="name"
|
||||
aria-label="{{ rc-i18n "Name" }}"
|
||||
name="name"
|
||||
value="{{item.name}}"
|
||||
{{disabled meta.limited}}
|
||||
autocomplete="off"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-action="openRichEditor"
|
||||
data-path="system.description"
|
||||
data-uuid="{{item.uuid}}"
|
||||
data-collaborative="true"
|
||||
>
|
||||
{{ rc-i18n "RipCrypt.Apps.edit-description" }}
|
||||
</button>
|
||||
<div class="value">{{{ enriched.system.description }}}</div>
|
||||
</div>
|
||||
35
templates/Apps/TraitSheet/style.css
Normal file
35
templates/Apps/TraitSheet/style.css
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
.ripcrypt.TraitSheet {
|
||||
--input-underline: none;
|
||||
max-width: 300px;
|
||||
|
||||
> .window-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 4px;
|
||||
color: var(--base-text);
|
||||
background: var(--base-background);
|
||||
}
|
||||
|
||||
input {
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.value {
|
||||
background: var(--input-background);
|
||||
color: var(--input-text);
|
||||
padding: 4px;
|
||||
|
||||
> :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
> :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
@import url("./common.css");
|
||||
@import url("./AllItemSheetV1/style.css");
|
||||
@import url("./CombinedHeroSheet/style.css");
|
||||
@import url("./DelveDiceHUD/style.css");
|
||||
|
|
@ -7,24 +8,8 @@
|
|||
@import url("./SkillsCardV1/style.css");
|
||||
@import url("./RichEditor/style.css");
|
||||
@import url("./ArmourSheet/style.css");
|
||||
@import url("./TraitSheet/style.css");
|
||||
@import url("./BookGeistSheet/style.css");
|
||||
|
||||
@import url("./popover.css");
|
||||
@import url("./popovers/AmmoTracker/style.css");
|
||||
|
||||
.ripcrypt {
|
||||
.window-content {
|
||||
flex: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.StatsCardV1,
|
||||
.SkillsCardV1,
|
||||
.CraftCardV1 {
|
||||
padding: 8px;
|
||||
/* height: 270px; */
|
||||
width: 680px;
|
||||
--col-gap: 2px;
|
||||
--row-gap: 4px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
templates/Apps/common.css
Normal file
17
templates/Apps/common.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.ripcrypt {
|
||||
.window-content {
|
||||
flex: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.StatsCardV1,
|
||||
.SkillsCardV1,
|
||||
.CraftCardV1 {
|
||||
padding: 8px;
|
||||
/* height: 270px; */
|
||||
width: 680px;
|
||||
--col-gap: 2px;
|
||||
--row-gap: 4px;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
Required parameters:
|
||||
"name" : the name of the item
|
||||
"system.quantity" : the quantity of the item
|
||||
"meta.idp" : the ID Prefix for the application
|
||||
--}}
|
||||
<header class="item-header">
|
||||
<div class="name-row">
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
.ripcrypt prose-mirror * {
|
||||
all: revert-layer;
|
||||
}
|
||||
|
|
@ -26,4 +26,3 @@
|
|||
@import url("../Apps/apps.css") layer(apps);
|
||||
|
||||
/* Exceptions */
|
||||
@import url("./elements/prose-mirror.css") layer(exceptions);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue