diff --git a/package/majimonsters-v0.2.zip b/package/majimonsters-v0.2.zip deleted file mode 100644 index 28d03c4..0000000 Binary files a/package/majimonsters-v0.2.zip and /dev/null differ diff --git a/package/ose-v0.1.zip b/package/ose-v0.1.zip new file mode 100644 index 0000000..e836a3a Binary files /dev/null and b/package/ose-v0.1.zip differ diff --git a/src/module/actor/monster-sheet.js b/src/module/actor/monster-sheet.js index 96d5b34..d4f08a9 100644 --- a/src/module/actor/monster-sheet.js +++ b/src/module/actor/monster-sheet.js @@ -1,5 +1,4 @@ import { OseActor } from "./entity.js"; -import { ActorTraitSelector } from "../apps/trait-selector.js"; /** * Extend the basic ActorSheet with some very simple modifications @@ -40,7 +39,7 @@ export class OseActorSheetMonster extends ActorSheet { */ getData() { const data = super.getData(); - data.config = CONFIG.MAJI; + data.config = CONFIG.OSE; // Prepare owned items this._prepareItems(data); @@ -52,18 +51,6 @@ export class OseActorSheetMonster extends ActorSheet { * @private */ _prepareItems(data) { - let [traits, techniques] = data.items.reduce( - (arr, item) => { - // Classify items into types - if (item.type === "feature") arr[0].push(item); - else if (item.type === "technique") arr[1].push(item); - return arr; - }, - [[], [], [], []] - ); - // Assign and return - data.traits = traits; - data.techniques = techniques; } _onItemSummary(event) { @@ -91,70 +78,6 @@ export class OseActorSheetMonster extends ActorSheet { } } - /** - * Handle spawning the ActorTraitSelector application which allows a checkbox of multiple trait options - * @param {Event} event The click event which originated the selection - * @private - */ - _onTraitSelector(event) { - event.preventDefault(); - const a = event.currentTarget; - const options = { - name: `data.${a.dataset.target}`, - title: a.innerText, - choices: CONFIG.MAJI[a.dataset.options], - }; - new ActorTraitSelector(this.actor, options).render(true); - } - - _keyUpHandler(event) { - if (event.keyCode == 17) { - let icons = document.querySelectorAll(".monster .roll-empowerable"); - for (let i = 0; i < icons.length; i++) { - let icon = icons[i].getElementsByTagName("img")[0]; - if (icon.getAttribute("src") == "/systems/majimonsters/assets/icons/dice/d6s.png") { - icon.setAttribute("src", "/systems/majimonsters/assets/icons/dice/d8s.png"); - } else if (icon.getAttribute("src") == "/systems/majimonsters/assets/icons/dice/d.png"){ - icon.setAttribute("src", "/systems/majimonsters/assets/icons/dice/dp.png"); - } - } - } - } - - _keyDownHandler(event) { - if (event.keyCode == 17) { - let icons = document.querySelectorAll(".monster .roll-empowerable"); - for (let i = 0; i < icons.length; i++) { - let icon = icons[i].getElementsByTagName("img")[0]; - if (icon.getAttribute("src") == "/systems/majimonsters/assets/icons/dice/d8s.png") { - icon.setAttribute("src", "/systems/majimonsters/assets/icons/dice/d6s.png"); - } else if (icon.getAttribute("src") == "/systems/majimonsters/assets/icons/dice/dp.png"){ - icon.setAttribute("src", "/systems/majimonsters/assets/icons/dice/d.png"); - } - } - } - } - - _onRollTechnique(event) { - event.preventDefault(); - let itemId = event.currentTarget.parentElement.previousElementSibling.dataset.itemId; - const technique = this.actor.getOwnedItem(itemId); - return technique.roll({empowered: event.ctrlKey, type: event.currentTarget.dataset.rollType}); - } - - _onRollStat(event) { - event.preventDefault(); - let stat = event.currentTarget.parentElement.dataset.attribute; - this.actor.rollStat(stat, { event: event, empowered: event.ctrlKey }); - } - - _onShowCard(event) { - event.preventDefault(); - let itemId = event.currentTarget.closest(".item").dataset.itemId; - const technique = this.actor.getOwnedItem(itemId); - return technique.roll({empowered: event.ctrlKey, type: event.currentTarget.dataset.rollType}); - } - /* -------------------------------------------- */ /** @@ -196,60 +119,7 @@ export class OseActorSheetMonster extends ActorSheet { this._onItemSummary(event); }); - // Switch Gender - html.find(".mm_gender").click((event) => { - event.preventDefault(); - if (this.actor.data["data"].details.gender === "female") { - this.actor.data["data"].details.gender = "male"; - this.render(); - return; - } - this.actor.data["data"].details.gender = "female"; - this.render(); - }); - - // Config modifier - html.find(".mm_bullet").click((ev) => { - event.preventDefault(); - let attribute = $(ev.currentTarget).siblings(".modifier"); - if (attribute.hasClass("hidden")) { - attribute.removeClass("hidden"); - return; - } - attribute.addClass("hidden"); - }); - - // trait Selector - html.find(".trait-selector").click(this._onTraitSelector.bind(this)); - - // Listen to event preventing duplicate bindings - if (document.getElementsByClassName("monster").length == 1) { - document.addEventListener("keydown", this._keyUpHandler); - document.addEventListener("keyup", this._keyDownHandler); - } - - html.find(".roll-icon").click((event) => { - if (event.currentTarget.classList.contains("roll-technique")) { - this._onRollTechnique(event); - return; - } - this._onRollStat(event); - }); - - html.find(".item-show").click((event) => { - this._onShowCard(event); - }); - // Handle default listeners last so system listeners are triggered first super.activateListeners(html); } - - /** @override */ - async close(options) { - if (document.getElementsByClassName("monster").length == 1) { - document.removeEventListener("keydown", this._keyUpHandler); - document.removeEventListener("keyup", this._keyDownHandler); - } - return super.close(options); - } } diff --git a/src/module/apps/trait-selector.js b/src/module/apps/trait-selector.js deleted file mode 100644 index 2fdb789..0000000 --- a/src/module/apps/trait-selector.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * A specialized form used to select damage or condition types which apply to an Actor - * @type {FormApplication} - */ -export class ActorTraitSelector extends FormApplication { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - id: "trait-selector", - classes: ["maji"], - title: "Actor Trait Selection", - template: "systems/ose/templates/apps/trait-selector.html", - width: 320, - height: "auto", - choices: {}, - }); - } - - /* -------------------------------------------- */ - - /** - * Return a reference to the target attribute - * @type {String} - */ - get attribute() { - return this.options.name; - } - - /* -------------------------------------------- */ - - /** - * Provide data to the HTML template for rendering - * @type {Object} - */ - getData() { - // Get current values - let attr = getProperty(this.object.data, this.attribute); - // Populate choices - const choices = duplicate(this.options.choices); - for (let [k, v] of Object.entries(choices)) { - choices[k] = { - label: v, - chosen: attr.includes(k), - }; - } - - // Return data - return { choices: choices }; - } - - /* -------------------------------------------- */ - - /** - * Update the Actor object with new trait data processed from the form - * @private - */ - _updateObject(event, formData) { - const choices = []; - for (let [k, v] of Object.entries(formData)) { - if (v) { - choices.push(k); - } - } - this.object.update({[`${this.attribute}`]: choices}); - } -} diff --git a/src/module/item/entity.js b/src/module/item/entity.js index 3b7face..0c23cc0 100644 --- a/src/module/item/entity.js +++ b/src/module/item/entity.js @@ -12,245 +12,4 @@ export class OseItem extends Item { prepareData() { super.prepareData(); } - - static async create(data, options = {}) { - return super.create(data, options); - } - /* -------------------------------------------- */ - /** @override */ - async update(data, options = {}) { - return super.update(data, options); - } - - /* -------------------------------------------- */ - - static chatListeners(html) { - } - - /** - * Roll the item to Chat - * @return {Promise} - */ - async roll(options = {}) { - if (options.type == "chat") { - this.rollCard(options); - return; - } - if (options.type == "attack") { - this.rollAttack(options); - return; - } - if (options.type == "damage") { - this.rollDamage(options); - return; - } - if (options.type == "trigger") { - return; - } - } - - /* -------------------------------------------- */ - /* Item Rolls - Attack, Damage, Saves, Checks */ - /* -------------------------------------------- */ - async rollCard(options = {}) { - // Basic template rendering data - const token = this.actor.token; - const templateData = { - actor: this.actor, - tokenId: token ? `${token.scene._id}.${token.id}` : null, - item: this.data, - hasAttack: this.data.data.details.attack != "", - isHealing: this.data.data.tags.descriptor == "healing", - hasDamage: this.data.data.damage.die != 0, - hasTrigger: this.data.data.trigger.threshold != 0, - config: CONFIG.MAJI, - }; - // Render the chat card - const template = "systems/ose/templates/chat/technique-card.html"; - const html = await renderTemplate(template, templateData); - - // Basic chat message data - const chatData = { - user: game.user._id, - type: CONST.CHAT_MESSAGE_TYPES.OTHER, - content: html, - speaker: { - actor: this.actor._id, - token: this.actor.token, - alias: this.actor.name, - }, - }; - // Toggle default roll mode - let rollMode = game.settings.get("core", "rollMode"); - if (["gmroll", "blindroll"].includes(rollMode)) - chatData["whisper"] = ChatMessage.getWhisperIDs("GM"); - if (rollMode === "blindroll") chatData["blind"] = true; - - // Create the chat message - return ChatMessage.create(chatData); - } - - /** - * Place an attack roll using an item (weapon, feat, spell, or equipment) - * Rely upon the Dice5e.d20Roll logic for the core implementation - * - * @return {Promise.} A Promise which resolves to the created Roll instance - */ - rollAttack(options = {}) { - const itemData = this.data.data; - const actorData = this.actor.data.data; - if (this.type != "technique") return; - const label = this.name; - let parts = []; - if (options.empowered) { - parts.push("2d8"); - } else { - parts.push("2d6"); - } - parts.push( - actorData.attributes[itemData.details.attack].value + - actorData.attributes[itemData.details.attack].mod - ); - - let rollMode = game.settings.get("core", "rollMode"); - let roll = new Roll(parts.join(" + "), {}).roll(); - roll.toMessage( - { - speaker: ChatMessage.getSpeaker({ actor: this.actor }), - flavor: `${label} ${game.i18n.localize("MAJI.technique.attack")}`, - }, - { rollMode } - ); - return roll; - } - - rollDamage(options = {}) { - const itemData = this.data.data; - const actorData = this.actor.data.data; - if (this.type != "technique") return; - const label = this.name; - let parts = []; - if (itemData.damage.num && itemData.damage.die) { - parts.push(`${itemData.damage.num}d${itemData.damage.die}`); - } - if (itemData.damage.bonus) { - parts.push( - actorData.attributes[itemData.damage.bonus].value + - actorData.attributes[itemData.damage.bonus].mod - ); - } - if (options.empowered && itemData.damage.die) { - parts.push(`1d${itemData.damage.die}`); - } - if (actorData.affinities.includes(itemData.element)) { - parts.push(actorData.affinity.value + actorData.affinity.mod); - } - let rollMode = game.settings.get("core", "rollMode"); - let roll = new Roll(parts.join(" + "), {}).roll(); - roll.toMessage( - { - speaker: ChatMessage.getSpeaker({ actor: this.actor }), - flavor: `${label} ${game.i18n.localize("MAJI.technique.damage")}`, - }, - { rollMode } - ); - return roll; - } - - /* -------------------------------------------- */ - - /** - * Handle toggling the visibility of chat card content when the name is clicked - * @param {Event} event The originating click event - * @private - */ - static _onChatCardToggleContent(event) { - event.preventDefault(); - const header = event.currentTarget; - const card = header.closest(".chat-card"); - const content = card.querySelector(".card-content"); - content.style.display = content.style.display === "none" ? "block" : "none"; - } - - static async _onChatCardAction(event) { - event.preventDefault(); - - // Extract card data - const button = event.currentTarget; - button.disabled = true; - const card = button.closest(".chat-card"); - const messageId = card.closest(".message").dataset.messageId; - const message = game.messages.get(messageId); - const action = button.dataset.action; - - // Validate permission to proceed with the roll - const isTargetted = action === "trigger"; - if (!(isTargetted || game.user.isGM || message.isAuthor)) return; - - // Get the Actor from a synthetic Token - const actor = this._getChatCardActor(card); - if (!actor) return; - - // Get the Item - const item = actor.getOwnedItem(card.dataset.itemId); - - // Get card targets - let targets = []; - if (isTargetted) { - targets = this._getChatCardTargets(card); - if (!targets.length) { - ui.notifications.warn( - `You must have one or more controlled Tokens in order to use this option.` - ); - return (button.disabled = false); - } - } - - // Attack and Damage Rolls - if (action === "attack") await item.rollAttack({ event }); - // Saving Throws for card targets - else if (action === "trigger") { - for (let t of targets) { - await t.rollTrigger( - { - threshold: button.dataset.threshold, - condition: button.dataset.condition, - }, - { event } - ); - } - } - - // Re-enable the button - button.disabled = false; - } - - static _getChatCardActor(card) { - // Case 1 - a synthetic actor from a Token - const tokenKey = card.dataset.tokenId; - if (tokenKey) { - const [sceneId, tokenId] = tokenKey.split("."); - const scene = game.scenes.get(sceneId); - if (!scene) return null; - const tokenData = scene.getEmbeddedEntity("Token", tokenId); - if (!tokenData) return null; - const token = new Token(tokenData); - return token.actor; - } - - // Case 2 - use Actor ID directory - const actorId = card.dataset.actorId; - return game.actors.get(actorId) || null; - } - - static _getChatCardTargets(card) { - const character = game.user.character; - const controlled = canvas.tokens.controlled; - const targets = controlled.reduce( - (arr, t) => (t.actor ? arr.concat([t.actor]) : arr), - [] - ); - if (character && controlled.length === 0) targets.push(character); - return targets; - } } diff --git a/src/module/item/item-sheet.js b/src/module/item/item-sheet.js index 04f49d3..464cd64 100644 --- a/src/module/item/item-sheet.js +++ b/src/module/item/item-sheet.js @@ -45,23 +45,10 @@ export class OseItemSheet extends ItemSheet { */ getData() { const data = super.getData(); - data.config = CONFIG.MAJI; + data.config = CONFIG.OSE; return data; } - _onDrop(event) { - event.preventDefault(); - let data; - try { - data = JSON.parse(event.dataTransfer.getData("text/plain")); - } catch (err) { - return; - } - // Only handle Actor drops - if (data.type !== "Actor") return; - this.entity.update({ data: { monster: { id: data.id } } }); - } - /* -------------------------------------------- */ /** @@ -69,30 +56,6 @@ export class OseItemSheet extends ItemSheet { * @param html {HTML} The prepared HTML object ready to be rendered into the DOM */ activateListeners(html) { - if (this.item.type == "drajule") { - const bar = html.find(".monster-drop"); - bar[0].ondrop = this._onDrop.bind(this); - } - - html.find(".entity").click(event => { - event.preventDefault(); - const element = event.currentTarget; - const entityId = element.dataset.entityId; - const entity = game.actors.entities.find(f => f.id === entityId); - const sheet = entity.sheet; - if (sheet._minimized) return sheet.maximize(); - else return sheet.render(true); - }); super.activateListeners(html); } - /* -------------------------------------------- */ - - /** - * Implement the _updateObject method as required by the parent class spec - * This defines how to update the subject of the form when the form is submitted - * @private - */ - _updateObject(event, formData) { - return this.object.update(formData); - } } diff --git a/src/templates/actors/monster-sheet.html b/src/templates/actors/monster-sheet.html index 72d73e4..e69de29 100644 --- a/src/templates/actors/monster-sheet.html +++ b/src/templates/actors/monster-sheet.html @@ -1,39 +0,0 @@ -
-
- {{> "systems/ose/templates/actors/partials/monster-header.html"}} -
- {{! Navigation }} -
- {{! Sheet Tab Navigation }} - -
-
- {{editor content=data.details.biography target="data.details.biography" - button=true owner=owner editable=editable}} -
- {{! Attributes }} -
- {{> "systems/ose/templates/actors/partials/monster-attributes-tab.html"}} -
-
- {{> "systems/ose/templates/actors/partials/monster-traits-tab.html"}} -
-
- {{> "systems/ose/templates/actors/partials/monster-techniques-tab.html"}} -
-
-
-