diff --git a/module/actor-sheet.js b/module/actor-sheet.js index 37784eb..6914948 100644 --- a/module/actor-sheet.js +++ b/module/actor-sheet.js @@ -2,7 +2,7 @@ * Extend the basic ActorSheet with some very simple modifications * @extends {ActorSheet} */ -export class SimpleActorSheet extends ActorSheet { +export class BladesActorSheet extends ActorSheet { /** @override */ static get defaultOptions() { @@ -125,4 +125,69 @@ export class SimpleActorSheet extends ActorSheet { // Update the Actor return this.object.update(formData); } + + /** @override */ + _getFormData(form) { + const FD = new FormData(form); + const dtypes = {}; + const editorTargets = Object.keys(this.editors); + console.log('YAMAMMMMMMAAAAAAAAAAAAAA!!!!'); + // Always include checkboxes + for ( let el of form.elements ) { + if ( !el.name ) continue; + + // Handle Radio groups + if ( form[el.name] instanceof RadioNodeList ) { + + const inputs = Array.from(form[el.name]); + if ( inputs.every(i => i.disabled) ) FD.delete(k); + + let values = ""; + let type = "Checkboxes"; + values = inputs.map(i => i.checked ? i.value : false).filter(i => i); + + FD.set(el.name, JSON.stringify(values)); + dtypes[el.name] = 'Radio'; + } + + // Remove disabled elements + else if ( el.disabled ) FD.delete(el.name); + + // Checkboxes + else if ( el.type == "checkbox" ) { + FD.set(el.name, el.checked || false); + dtypes[el.name] = "Boolean"; + } + + // Include dataset dtype + else if ( el.dataset.dtype ) dtypes[el.name] = el.dataset.dtype; + } + + // Process editable images + for ( let img of form.querySelectorAll('img[data-edit]') ) { + if ( img.getAttribute("disabled") ) continue; + let basePath = window.location.origin+"/"; + if ( ROUTE_PREFIX ) basePath += ROUTE_PREFIX+"/"; + FD.set(img.dataset.edit, img.src.replace(basePath, "")); + } + + // Process editable divs (excluding MCE editors) + for ( let div of form.querySelectorAll('div[data-edit]') ) { + if ( div.getAttribute("disabled") ) continue; + else if ( editorTargets.includes(div.dataset.edit) ) continue; + FD.set(div.dataset.edit, div.innerHTML.trim()); + } + + // Handle MCE editors + Object.values(this.editors).forEach(ed => { + if ( ed.mce ) { + FD.delete(ed.mce.id); + if ( ed.changed ) FD.set(ed.target, ed.mce.getContent()); + } + }); + + // Record target data types for casting + FD._dtypes = dtypes; + return FD; + } } diff --git a/module/blades.js b/module/blades.js index a79e8f9..139f88e 100644 --- a/module/blades.js +++ b/module/blades.js @@ -6,14 +6,14 @@ // Import Modules import { SimpleItemSheet } from "./item-sheet.js"; -import { SimpleActorSheet } from "./actor-sheet.js"; +import { BladesActorSheet } from "./actor-sheet.js"; /* -------------------------------------------- */ /* Foundry VTT Initialization */ /* -------------------------------------------- */ Hooks.once("init", async function() { - console.log(`Initializing Simple Worldbuilding System`); + console.log(`Initializing Blades In the Dark System`); /** * Set an initiative formula for the system @@ -26,7 +26,7 @@ Hooks.once("init", async function() { // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("dnd5e", SimpleActorSheet, { makeDefault: true }); + Actors.registerSheet("dnd5e", BladesActorSheet, { makeDefault: true }); Items.unregisterSheet("core", ItemSheet); Items.registerSheet("dnd5e", SimpleItemSheet, {makeDefault: true}); @@ -55,7 +55,7 @@ Hooks.once("init", async function() { const rgx = new RegExp(' value=\"' + count + '\"'); return html.replace(rgx, "$& checked=\"checked\""); - + }); }); diff --git a/overrides/foundry.js b/overrides/foundry.js deleted file mode 100644 index 8adbef2..0000000 --- a/overrides/foundry.js +++ /dev/null @@ -1,42388 +0,0 @@ -/** - * The global CONSTANTS object - * @type {Object} - */ -const CONST = { - vtt: "Foundry VTT", - VTT: "Foundry Virtual Tabletop", - WEBSITE_URL: "https://foundryvtt.com" -}; - -/** - * Define the string name used for the base entity type when specific sub-types are not defined by the system - * @type {String} - */ -CONST.BASE_ENTITY_TYPE = "base"; - -/** - * Valid Chat Message types - * @type {Object} - */ -CONST.CHAT_MESSAGE_TYPES = { - OTHER: 0, - OOC: 1, - IC: 2, - EMOTE: 3, - WHISPER: 4, - ROLL: 5 -}; - -/** - * The allowed Entity types which may exist within a Compendium pack - * This is a subset of ENTITY_TYPES - * @type {Array} - */ -CONST.COMPENDIUM_ENTITY_TYPES = ["Actor", "Item", "Scene", "JournalEntry", "Macro", "RollTable", "Playlist"]; - -/** - * The default artwork used for Token images if none is provided - * @type {String} - */ -CONST.DEFAULT_TOKEN = 'icons/svg/mystery-man.svg'; - -/** - * The default artwork used for Note placeables if none is provided - * @type {String} - */ -CONST.DEFAULT_NOTE_ICON = 'icons/svg/book.svg'; - -/** - * The supported dice roll visibility modes - * @type {Object} - */ -CONST.DICE_ROLL_MODES = { - PUBLIC: "roll", - PRIVATE: "gmroll", - BLIND: "blindroll", - SELF: "selfroll" -}; - - -/* -------------------------------------------- */ - - -/** - * The allowed Drawing types which may be saved - * @type {Object} - */ -CONST.DRAWING_TYPES = { - RECTANGLE: "r", - ELLIPSE: "e", - TEXT: "t", - POLYGON: "p", - FREEHAND: "f" -}; - -/** - * The allowed fill types which a Drawing object may display - * NONE: The drawing is not filled - * SOLID: The drawing is filled with a solid color - * PATTERN: The drawing is filled with a tiled image pattern - * @type {Object} - */ -CONST.DRAWING_FILL_TYPES = { - NONE: 0, - SOLID: 1, - PATTERN: 2 -}; - - -/** - * The default configuration values used for Drawing objects - * @type {Object} - */ -CONST.DRAWING_DEFAULT_VALUES = { - width: 0, - height: 0, - rotation: 0, - z: 0, - hidden: false, - locked: false, - fillType: CONST.DRAWING_FILL_TYPES.NONE, - fillAlpha: 0.5, - bezierFactor: 0.1, - strokeAlpha: 1.0, - strokeWidth: 8, - fontSize: 48, - textAlpha: 1.0, - textColor: "#FFFFFF" -}; - - -/* -------------------------------------------- */ - - - -/** - * Define the allowed Entity class types - * @type {Array} - */ -CONST.ENTITY_TYPES = [ - "Actor", - "ChatMessage", - "Combat", - "Item", - "Folder", - "JournalEntry", - "Macro", - "Playlist", - "RollTable", - "Scene", - "User", -]; - -/** - * EULA version number - * @type {String} - */ -CONST.EULA_VERSION = "0.4.7"; - -/** - * Define the allowed permission levels for a non-user Entity. - * Each level is assigned a value in ascending order. Higher levels grant more permissions. - * @type {Object} - */ -CONST.ENTITY_PERMISSIONS = { - "NONE": 0, - "LIMITED": 1, - "OBSERVER": 2, - "OWNER": 3 -}; - -/** - * The maximum allowed level of depth for Folder nesting - * @type {Number} - */ -CONST.FOLDER_MAX_DEPTH = 3; - -/** - * Define the allowed Entity types which Folders may contain - * @type {Array} - */ -CONST.FOLDER_ENTITY_TYPES = ["Actor", "Item", "Scene", "JournalEntry", "RollTable"]; - -/** - * Define the allowed Entity types which may be dynamically linked in chat - * @type {Array} - */ -CONST.ENTITY_LINK_TYPES = ["Actor", "Item", "Scene", "JournalEntry", "Macro", "RollTable"]; - -/** - * The allowed Grid types which are supported by the software - * @type {Object} - */ -CONST.GRID_TYPES = { - "GRIDLESS": 0, - "SQUARE": 1, - "HEXODDR": 2, - "HEXEVENR": 3, - "HEXODDQ": 4, - "HEXEVENQ": 5 -}; - -/** - * The minimum allowed grid size which is supported by the software - * @type {Number} - */ -CONST.GRID_MIN_SIZE = 50; - - -/** - * An Array of valid MacroAction scope values - * @type {Array.} - */ -CONST.MACRO_SCOPES = ["global", "actors", "actor"]; - - -/** - * The allowed playback modes for an audio Playlist - * DISABLED: The playlist does not play on its own, only individual Sound tracks played as a soundboard - * SEQUENTIAL: The playlist plays sounds one at a time in sequence - * SHUFFLE: The playlist plays sounds one at a time in randomized order - * SIMULTANEOUS: The playlist plays all contained sounds at the same time - * @type {Object} - */ -CONST.PLAYLIST_MODES = { - "DISABLED": -1, - "SEQUENTIAL": 0, - "SHUFFLE": 1, - "SIMULTANEOUS": 2 -}; - - -/** - * Define the threshold version which packages must support as their minimumCoreVersion in order to be usable - * @type {string} - */ -CONST.REQUIRED_PACKAGE_CORE_VERSION = "0.4.4"; - - -/** - * Encode the reasons why a package may be available or unavailable for use - * @type {Object} - */ -CONST.PACKAGE_AVAILABILITY_CODES = { - "AVAILABLE": 0, - "REQUIRES_UPDATE": 1, - "REQUIRES_SYSTEM": 2, - "REQUIRES_DEPENDENCY": 3, - "REQUIRES_CORE": 4 -}; - -/** - * A safe password string which can be displayed - */ -CONST.PASSWORD_SAFE_STRING = "•".repeat(16); - - -/** - * The default sorting density for manually ordering child objects within a parent - * @type {Number} - */ -CONST.SORT_INTEGER_DENSITY = 100000; - -/** - * The allowed types of a TableResult document - * @type {Object} - */ -CONST.TABLE_RESULT_TYPES = { - TEXT: 0, - ENTITY: 1, - COMPENDIUM: 2 -}; - -/** - * Define the valid anchor locations for a Tooltip displayed on a Placeable Object - * @type {Object} - */ -CONST.TEXT_ANCHOR_POINTS = { - CENTER: 0, - BOTTOM: 1, - TOP: 2, - LEFT: 3, - RIGHT: 4 -}; - -/** - * Describe the various thresholds of token control upon which to show certain pieces of information - * NONE - no information is displayed - * CONTROL - displayed when the token is controlled - * OWNER HOVER - displayed when hovered by a GM or a user who owns the actor - * HOVER - displayed when hovered by any user - * OWNER - always displayed for a GM or for a user who owns the actor - * ALWAYS - always displayed for everyone - * @type {Object} - */ -CONST.TOKEN_DISPLAY_MODES = { - "NONE": 0, - "CONTROL": 10, - "OWNER_HOVER": 20, - "HOVER": 30, - "OWNER": 40, - "ALWAYS": 50 -}; - -/** - * The allowed Token disposition types - * HOSTILE - Displayed as an enemy with a red border - * NEUTRAL - Displayed as neutral with a yellow border - * FRIENDLY - Displayed as an ally with a cyan border - */ -CONST.TOKEN_DISPOSITIONS = { - "HOSTILE": -1, - "NEUTRAL": 0, - "FRIENDLY": 1 -}; - -/** - * Define the allowed User permission levels. - * Each level is assigned a value in ascending order. Higher levels grant more permissions. - * @type {Object} - */ -CONST.USER_ROLES = { - "NONE": 0, - "PLAYER": 1, - "TRUSTED": 2, - "ASSISTANT": 3, - "GAMEMASTER": 4 -}; - -/** - * Invert the User Role mapping to recover role names from a role integer - * @type {Object} - */ -CONST.USER_ROLE_NAMES = Object.entries(CONST.USER_ROLES).reduce((obj, r) => { - obj[r[1]] = r[0]; - return obj; -}, {}); - - -/** - * Define the named actions which users or user roles can be permitted to do. - * Each key of this Object denotes an action for which permission may be granted (true) or withheld (false) - * @type {Object} - */ -CONST.USER_PERMISSIONS = { - "BROADCAST_AUDIO": { - label: "PERMISSION.BroadcastAudio", - hint: "PERMISSION.BroadcastAudioHint", - disableGM: true, - defaultRole: CONST.USER_ROLES.TRUSTED - }, - "BROADCAST_VIDEO": { - label: "PERMISSION.BroadcastVideo", - hint: "PERMISSION.BroadcastVideoHint", - disableGM: true, - defaultRole: CONST.USER_ROLES.TRUSTED - }, - "ACTOR_CREATE": { - label: "PERMISSION.ActorCreate", - hint: "PERMISSION.ActorCreateHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.ASSISTANT - }, - "DRAWING_CREATE": { - label: "PERMISSION.DrawingCreate", - hint: "PERMISSION.DrawingCreateHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.TRUSTED - }, - "ITEM_CREATE": { - label: "PERMISSION.ItemCreate", - hint: "PERMISSION.ItemCreateHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.ASSISTANT - }, - "FILES_BROWSE": { - label: "PERMISSION.FilesBrowse", - hint: "PERMISSION.FilesBrowseHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.TRUSTED - }, - "FILES_UPLOAD": { - label: "PERMISSION.FilesUpload", - hint: "PERMISSION.FilesUploadHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.ASSISTANT - }, - "MACRO_SCRIPT": { - label: "PERMISSION.MacroScript", - hint: "PERMISSION.MacroScriptHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.PLAYER - }, - "MESSAGE_WHISPER": { - label: "PERMISSION.MessageWhisper", - hint: "PERMISSION.MessageWhisperHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.PLAYER - }, - "SETTINGS_MODIFY": { - label: "PERMISSION.SettingsModify", - hint: "PERMISSION.SettingsModifyHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.ASSISTANT - }, - "SHOW_CURSOR": { - label: "PERMISSION.ShowCursor", - hint: "PERMISSION.ShowCursorHint", - disableGM: true, - defaultRole: CONST.USER_ROLES.PLAYER - }, - "TEMPLATE_CREATE": { - label: "PERMISSION.TemplateCreate", - hint: "PERMISSION.TemplateCreateHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.PLAYER - }, - "TOKEN_CREATE": { - label: "PERMISSION.TokenCreate", - hint: "PERMISSION.TokenCreateHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.ASSISTANT - }, - "TOKEN_CONFIGURE": { - label: "PERMISSION.TokenConfigure", - hint: "PERMISSION.TokenConfigureHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.TRUSTED - }, - "WALL_DOORS": { - label: "PERMISSION.WallDoors", - hint: "PERMISSION.WallDoorsHint", - disableGM: false, - defaultRole: CONST.USER_ROLES.PLAYER - } -}; - - -/** - * The allowed directions of effect that a Wall can have - * BOTH: The wall collides from both directions - * LEFT: The wall collides only when a ray strikes its left side - * RIGHT: The wall collides only when a ray strikes its right side - * @type {Object} - */ -CONST.WALL_DIRECTIONS = { - BOTH: 0, - LEFT: 1, - RIGHT: 2 -}; - -/** - * The allowed door types which a Wall may contain - * NONE: The wall does not contain a door - * DOOR: The wall contains a regular door - * SECRET: The wall contains a secret door - * @type {Object} - */ -CONST.WALL_DOOR_TYPES = { - NONE: 0, - DOOR: 1, - SECRET: 2 -}; - -/** - * The allowed door states which may describe a Wall that contains a door - * CLOSED: The door is closed - * OPEN: The door is open - * LOCKED: The door is closed and locked - * @type {Object} - */ -CONST.WALL_DOOR_STATES = { - CLOSED: 0, - OPEN: 1, - LOCKED: 2 -}; - -/** - * The types of movement collision which a Wall may impose - * NONE: Movement does not collide with this wall - * NORMAL: Movement collides with this wall - * @type {Object} - */ -CONST.WALL_MOVEMENT_TYPES = { - NONE: 0, - NORMAL: 1 -}; - -/** - * The types of sensory collision which a Wall may impose - * NONE: Senses do not collide with this wall - * NORMAL: Senses collide with this wall - * LIMITED: Senses collide with the second intersection, bypassing the first - * @type {Object} - */ -CONST.WALL_SENSE_TYPES = { - NONE: 0, - NORMAL: 1, - LIMITED: 2 -}; - -/** - * The supported file extensions for image-type files - * @type {Array} - */ -CONST.IMAGE_FILE_EXTENSIONS = ["jpg", "jpeg", "png", "svg", "webp"]; - -/** - * The supported file extensions for video-type files - * @type {Array} - */ -CONST.VIDEO_FILE_EXTENSIONS = ["mp4", "ogg", "webm"]; - -/** - * The supported file extensions for audio-type files - * @type {Array} - */ -CONST.AUDIO_FILE_EXTENSIONS = ["flac", "mp3", "ogg", "wav", "webm"]; - -// Freeze the CONST object to prevent changes -Object.freeze(CONST); - -try { -module.exports = CONST; -} catch(err) {} - -try { -window.CONST = CONST; -} catch(err) {} - -/* -------------------------------------------- */ -/* Math Functions */ -/* -------------------------------------------- */ - -Math.clamped = function(x, min, max) { - return Math.min(max, Math.max(x, min)); -}; - -Math.decimals = function(number, places) { - let scl = Math.pow(10, places); - return Math.round(number * scl) / scl; -}; - -toDegrees = function(angle) { - return angle * (180 / Math.PI); -}; - -normalizeDegrees = function(degrees) { - let nd = (degrees + 360) % 360; - return (nd > 180) ? nd - 360 : nd; -}; - -toRadians = function(degree) { - return (Math.round(degree) % 360) * (Math.PI / 180); -}; - -normalizeRadians = function(rad) { - let pi2 = 2 * Math.PI; - let nr = (rad + pi2) % pi2; - return (nr > Math.PI) ? nr - pi2 : nr; -}; - -/* -------------------------------------------- */ -/* String Methods */ -/* -------------------------------------------- */ - - -String.prototype.capitalize = function() { - if ( !this.length ) return this; - return this.charAt(0).toUpperCase() + this.slice(1); -}; - - -String.prototype.titleCase = function() { - if (!this.length) return this; - return this.toLowerCase().split(' ').map(function (word) { - return word.replace(word[0], word[0].toUpperCase()); - }).join(' '); -}; - - -/** - * Strip any