Tidy the worldbuilding system for 0.40 release

This commit is contained in:
Andrew 2020-10-19 17:07:23 -07:00
parent d641ff96b0
commit 6bf056b3d4
9 changed files with 72 additions and 184 deletions

View file

@ -24,11 +24,7 @@ export class SimpleActorSheet extends ActorSheet {
/** @override */
getData() {
const data = super.getData();
// Handle attribute groups.
EntitySheetHelper.getAttributeData(data);
// Add shorthand.
data.shorthand = !!game.settings.get("worldbuilding", "macroShorthand");
return data;
}
@ -39,15 +35,13 @@ export class SimpleActorSheet extends ActorSheet {
activateListeners(html) {
super.activateListeners(html);
// Handle rollable items.
html.find(".items .rollable").on("click", this._onItemRoll.bind(this));
// Handle rollable attributes.
html.find(".attributes").on("click", "a.attribute-roll", EntitySheetHelper.onAttributeRoll.bind(this));
// Everything below here is only needed if the sheet is editable
if ( !this.options.editable ) return;
// Handle rollable items and attributes
html.find(".items .rollable").on("click", this._onItemRoll.bind(this));
html.find(".attributes").on("click", "a.attribute-roll", EntitySheetHelper.onAttributeRoll.bind(this));
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item");
@ -80,36 +74,6 @@ export class SimpleActorSheet extends ActorSheet {
/* -------------------------------------------- */
/** @override */
async _onSubmit(event, {updateData=null, preventClose=false, preventRender=false}={}) {
let attr = EntitySheetHelper.onSubmit(event);
// Submit the form if attr is true or an attr key.
if ( attr ) {
await super._onSubmit(event, {updateData: updateData, preventClose: preventClose, preventRender: preventRender});
// If attr is a key and not just true, set a very short timeout and retrigger focus after the original element is deleted and the new one is inserted.
if ( attr !== true) {
setTimeout(() => {
$(`input[name="${attr}"]`).parents('.attribute').find('.attribute-value').focus();
}, 10);
}
}
}
/* -------------------------------------------- */
/** @override */
setPosition(options={}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/**
* Listen for roll buttons on items.
* @param {MouseEvent} event The originating left click event
@ -126,14 +90,12 @@ export class SimpleActorSheet extends ActorSheet {
});
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Handle attribute and group updates.
formData = EntitySheetHelper.updateAttributes(formData, this);
formData = EntitySheetHelper.updateGroups(formData, this);
// Update the Actor with the new form values.
return this.object.update(formData);
}

View file

@ -181,7 +181,7 @@ export class EntitySheetHelper {
const label = button.closest(".attribute").querySelector(".attribute-label")?.value;
const chatLabel = label ?? button.parentElement.querySelector(".attribute-key").value;
const shorthand = game.settings.get("worldbuilding", "macroShorthand");
const rollData = this.actor.getRollData();
const rollData = this.object.getRollData();
let formula = button.closest(".attribute").querySelector(".attribute-value")?.value;
// If there's a formula, attempt to roll it.
@ -527,18 +527,14 @@ export class EntitySheetHelper {
if ( typeof formula != "string" || depth < 1) {
return 0;
}
// Replace attributes with their numeric equivalents.
let dataRgx = new RegExp(/@([a-z.0-9_\-]+)/gi);
let rollFormula = formula.replace(dataRgx, (match, term) => {
// Replace matches with the value, or the missing value.
let value = getProperty(data, term);
value = value ? String(value).trim() : (missing != null ? missing : `@${term}`);
// If there's still an attribute in the returned string, nest it in parentheses so that it's evaluated first in the roll.
value = value && value.includes('@') ? `(${value})` : value;
return value;
if ( value === null ) return "0";
if ( String(value).includes('@') ) return value;
else return `@${term}`;
});
return rollFormula;
}

View file

@ -23,55 +23,22 @@ export class SimpleItemSheet extends ItemSheet {
/** @override */
getData() {
const data = super.getData();
// Handle attribute groups.
EntitySheetHelper.getAttributeData(data);
return data;
}
/* -------------------------------------------- */
/** @override */
async _onSubmit(event, {updateData=null, preventClose=false, preventRender=false}={}) {
let attr = EntitySheetHelper.onSubmit(event);
// Submit the form if attr is true or an attr key.
if ( attr ) {
await super._onSubmit(event, {updateData: updateData, preventClose: preventClose, preventRender: preventRender});
// If attr is a key and not just true, set a very short timeout and retrigger focus after the original element is deleted and the new one is inserted.
if ( attr !== true) {
setTimeout(() => {
$(`input[name="${attr}"]`).parents('.attribute').find('.attribute-value').focus();
}, 10);
}
}
}
/* -------------------------------------------- */
/** @override */
setPosition(options={}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Handle rollable attributes.
html.find(".attributes").on("click", "a.attribute-roll", EntitySheetHelper.onAttributeRoll.bind(this));
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Rollable attributes
html.find(".attributes").on("click", "a.attribute-roll", EntitySheetHelper.onAttributeRoll.bind(this));
// Add draggable for macros.
html.find(".attributes a.attribute-roll").each((i, a) => {
a.setAttribute("draggable", true);

View file

@ -176,110 +176,66 @@ Hooks.on("getItemDirectoryEntryContext", (html, options) => {
});
/**
* Adds the actor template selection dialog.
*/
ActorDirectory.prototype._onCreateEntity = async (event) => {
// Do not allow the creation event to bubble to other listeners
async function _onCreateEntity(event) {
event.preventDefault();
event.stopPropagation();
_simpleDirectoryTemplates('actor', event);
}
/**
* Adds the item template selection dialog.
*/
ItemDirectory.prototype._onCreateEntity = async (event) => {
// Do not allow the creation event to bubble to other listeners
event.preventDefault();
event.stopPropagation();
_simpleDirectoryTemplates('item', event);
return _simpleDirectoryTemplates(this, event);
}
ActorDirectory.prototype._onCreateEntity = _onCreateEntity; // For 0.7.x+
ItemDirectory.prototype._onCreateEntity = _onCreateEntity;
ActorDirectory.prototype._onCreate = _onCreateEntity; // TODO: for 0.6.6
ItemDirectory.prototype._onCreate = _onCreateEntity;
/**
* Display the entity template dialog.
*
* Helper function to display a dialog if there are multiple template types
* defined for the entity type.
*
* @param {string} entityType - 'actor' or 'item'
* Helper function to display a dialog if there are multiple template types defined for the entity type.
* TODO: Refactor in 0.7.x to play more nicely with the Entity.createDialog method
*1
* @param {EntityCollection} entityType - The sidebar tab
* @param {MouseEvent} event - Triggering event
*/
async function _simpleDirectoryTemplates(entityType = 'actor', event) {
// Retrieve the collection and class.
const entityCollection = entityType == 'actor' ? game.actors : game.items;
const cls = entityType == 'actor' ? Actor : Item;
async function _simpleDirectoryTemplates(collection, event) {
// Query for all entities of this type using the "isTemplate" flag.
let entities = entityCollection.filter(a => a.data.flags?.worldbuilding?.isTemplate === true);
// Initialize variables related to the entity class.
// Retrieve the collection and find any available templates
const entityCollection = collection.tabName === "actors" ? game.actors : game.items;
const cls = collection.tabName === "actors" ? Actor : Item;
let templates = entityCollection.filter(a => a.getFlag("worldbuilding", "isTemplate"));
let ent = game.i18n.localize(cls.config.label);
// Setup entity data.
let type = entityType == 'actor' ? 'character' : 'item';
// Setup default creation data
let type = collection.tabName === "actors" ? 'character' : 'item';
let createData = {
name: `${game.i18n.localize("SIMPLE.New")} ${ent}`,
type: type,
folder: event.currentTarget.dataset.folder
};
if ( !templates.length ) return cls.create(createData, {renderSheet: true});
// If there's more than one entity template type, create a form.
if (entities.length > 0) {
// Build an array of types for the form, including an empty default.
let types = [{
value: null,
label: game.i18n.localize("SIMPLE.NoTemplate")
}];
// Build an array of types for the form, including an empty default.
let types = [{
value: null,
label: game.i18n.localize("SIMPLE.NoTemplate")
}].concat(templates.map(a => { return { value: a.id, label: a.name } }));
// Append each of the user-defined actor/item types.
types = types.concat(entities.map(a => {
return {
value: a.data.name,
label: a.data.name
// Render the confirmation dialog window
const templateData = {upper: ent, lower: ent.toLowerCase(), types: types};
const dlg = await renderTemplate(`systems/worldbuilding/templates/sidebar/entity-create.html`, templateData);
return Dialog.confirm({
title: `${game.i18n.localize("SIMPLE.Create")} ${createData.name}`,
content: dlg,
yes: html => {
const form = html[0].querySelector("form");
const template = entityCollection.get(form.type.value);
if ( template ) {
createData = mergeObject(template.data, createData, {inplace: false});
createData.type = template.data.type;
delete createData.flags.worldbuilding.isTemplate;
}
}));
// Render the entity creation form
let templateData = {upper: ent, lower: ent.toLowerCase(), types: types},
dlg = await renderTemplate(`systems/worldbuilding/templates/sidebar/entity-create.html`, templateData);
// Render the confirmation dialog window
Dialog.confirm({
title: `${game.i18n.localize("SIMPLE.Create")} ${createData.name}`,
content: dlg,
yes: html => {
// Get the form data.
const form = html[0].querySelector("form");
const fd = new FormDataExtended(form);
mergeObject(createData, fd.toObject());
// Store the type and name values, and retrieve the template entity.
let templateActor = entityCollection.getName(createData.type);
// If there's a template entity, handle the data.
if (templateActor) {
// Update the object with the existing template's values.
createData = mergeObject(templateActor.data, createData, {inplace: false});
createData.type = templateActor.data.type;
// Clear the flag so that this doesn't become a new template.
delete createData.flags.worldbuilding.isTemplate;
}
// Otherwise, restore to a valid entity type (character/item).
else {
createData.type = type;
}
cls.create(createData, {renderSheet: true});
},
no: () => {},
defaultYes: false
});
}
// Otherwise, just create a blank entity.
else {
cls.create(createData, {renderSheet: true});
}
createData.name = form.name.value;
return cls.create(createData, {renderSheet: true});
},
no: () => {},
defaultYes: false
});
}

View file

@ -8,8 +8,11 @@
padding: 5px;
overflow-y: hidden;
}
.worldbuilding form {
height: 100%;
}
.worldbuilding .sheet-header {
height: 100px;
flex: 0 0 100px;
overflow: hidden;
display: flex;
flex-direction: row;
@ -49,7 +52,7 @@
height: 28px;
}
.worldbuilding .tabs {
height: 40px;
flex: 0 0 40px;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;
}

View file

@ -5,8 +5,12 @@
overflow-y: hidden;
}
form {
height: 100%;
}
.sheet-header {
height: 100px;
flex: 0 0 100px;
overflow: hidden;
display: flex;
flex-direction: row;
@ -52,7 +56,7 @@
/* Sheet Tabs */
.tabs {
height: 40px;
flex: 0 0 40px;
border-top: 1px solid #AAA;
border-bottom: 1px solid #AAA;

View file

@ -1,9 +1,9 @@
{
"name": "worldbuilding",
"title": "Simple World-Building",
"description": "A minimalist game system with very simple Actor and Item models to support free-form system agnostic gameplay.",
"version": 0.36,
"minimumCoreVersion": "0.7.3",
"description": "A minimalist game system which provides configurable Actor and Item templates to support free-form system agnostic game-play.",
"version": 0.40,
"minimumCoreVersion": "0.6.6",
"compatibleCoreVersion": "0.7.3",
"templateVersion": 2,
"author": "Atropos",
@ -23,6 +23,6 @@
"secondaryTokenAttribute": "power",
"url": "https://gitlab.com/foundrynet/worldbuilding/",
"manifest": "https://gitlab.com/foundrynet/worldbuilding/raw/master/system.json",
"download": "https://gitlab.com/foundrynet/worldbuilding/-/archive/release-036/worldbuilding-release-036.zip",
"download": "https://gitlab.com/foundrynet/worldbuilding/-/archive/release-040/worldbuilding-release-040.zip",
"license": "LICENSE.txt"
}

View file

@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<form class="flexcol {{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">

View file

@ -1,4 +1,4 @@
<form class="{{cssClass}}" autocomplete="off">
<form class="flexcol {{cssClass}}" autocomplete="off">
<header class="sheet-header">
<img class="profile-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
<div class="header-fields">