diff --git a/css/mosh.css b/css/mosh.css index e827cb3..56a0dbb 100644 --- a/css/mosh.css +++ b/css/mosh.css @@ -1702,4 +1702,36 @@ p { .mosh .items-tab-grid{ grid-template-columns: 50% 50%; +} +.mosh form.actor-generator div.fulllabel { + border-radius: 2em; +} +.mosh form.actor-generator div.ulinputfield { + min-height: 150px; +} +.mosh form.actor-generator .smalltext { + font-size: 0.7rem; +} +.mosh form.actor-generator div#generator-header-grid-1col { + grid-column: span 1 / span 1; + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.mosh form.actor-generator .element-group { + margin-top: 10px; +} + +.mosh form.actor-generator .credit-img{ + height: 30px; + margin-top: 0%; +} + +.mosh form.actor-generator .checkbox-round { + appearance: none; +} +.mosh form.actor-generator .checkbox-round:checked { + background-color: gray; +} +.mosh form.actor-generator .clicable-item{ + cursor: pointer; } \ No newline at end of file diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js index 8286c35..c3a6679 100644 --- a/module/actor/actor-sheet.js +++ b/module/actor/actor-sheet.js @@ -1,3 +1,4 @@ +import { DLActorGenerator } from "../windows/actor-generator.js"; /** * Extend the basic ActorSheet with some very simple modifications * @extends {ActorSheet} @@ -633,6 +634,28 @@ export class MothershipActorSheet extends ActorSheet { }); } - - + /** + * Extend and override the sheet header buttons + * @override + */ + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + const canConfigure = game.user.isGM || this.actor.isOwner; + if (this.options.editable && canConfigure) { + buttons = [{ + label: 'Character Generator', + class: 'configure-actor', + icon: 'fas fa-cogs', + onclick: (ev) => this._onConfigureCreature(ev), + },].concat(buttons); + } + return buttons; + } + _onConfigureCreature(event) { + event.preventDefault(); + new DLActorGenerator(this.actor, { + top: this.position.top + 40, + left: this.position.left + (this.position.width - 400) / 2 + }).render(true); + } } diff --git a/module/actor/actor.js b/module/actor/actor.js index 969f368..5740ada 100644 --- a/module/actor/actor.js +++ b/module/actor/actor.js @@ -3192,6 +3192,11 @@ export class MothershipActor extends Actor { await this.createEmbeddedDocuments('Item', [itemData]); //create message text flavorText = `You add this to your inventory.`; + } else if (itemData.type === 'skill' ) { + //add item to the players inventory + await this.createEmbeddedDocuments('Item', [itemData]); + //create message text + flavorText = `You learn this skill.`; } } //generate chat message diff --git a/module/windows/actor-generator.js b/module/windows/actor-generator.js new file mode 100644 index 0000000..7c37418 --- /dev/null +++ b/module/windows/actor-generator.js @@ -0,0 +1,431 @@ +export class DLActorGenerator extends FormApplication { + static get defaultOptions() { + const options = super.defaultOptions; + options.id = 'sheet-modifiers'; + options.classes = ["mosh", "sheet", "actor", "character"]; + options.template = 'systems/mosh/templates/dialogs/actor-generator-dialog.html'; + options.width = 800; + options.dragDrop = [{dragSelector: null, dropSelector: ".dropitem"}]; + return options; + } + + /* -------------------------------------------- */ + /** + * Add the Entity name into the window title + * @type {String} + */ + get title() { + return `${this.object.name}: Character Generator`; + } + /* -------------------------------------------- */ + + /** + * Construct and return the data object used to render the HTML template for this form application. + * @return {Object} + */ + getData() { + //if (this.object.system.class.uuid != null){ + // this.updateClass(this.object.system.class.uuid); + //} + let data=this.object; + data.class_options = game.items.filter(i => i.type=="class"); + data.system.class.value = ""; + return data; + } + /* -------------------------------------------- */ + + async rollDices(dices, html, id,chatmsg="") { + + if (html.find(`img[id="` + id + `"]`).prop('hidden') == false) { + let roll = await new Roll(dices).roll(); + if (chatmsg != ""){ + await roll.toMessage({flavor: chatmsg}); + } + //console.log(id + "->" + roll.total) + html.find(`img[id="` + id + `"]`).prop('hidden', true); + html.find(`input[id="` + id + `"]`).prop('hidden', false); + html.find(`input[id="` + id + `"]`).prop('value', roll.total); + } + } + + + /* todo: remove this functions and set the values as configuration */ + async rollStrength(html) { + this.rollDices("2d10+25", html, `system.stats.strength.value`,`Rolling for Strength stat`) + } + + async rollSpeed(html) { + this.rollDices("2d10+25", html, `system.stats.speed.value`,`Rolling for Speed stat`) + } + async rollIntellect(html) { + this.rollDices("2d10+25", html, `system.stats.intellect.value`,`Rolling for Intellect stat`) + } + async rollCombat(html) { + this.rollDices("2d10+25", html, `system.stats.combat.value`,`Rolling for Combat stat`) + } + async rollHealth(html) { + this.rollDices("1d10+10", html, `system.health.max`,`Rolling for Health`) + } + async rollSanity(html) { + this.rollDices("2d10+10", html, `system.stats.sanity.value`,`Rolling for Sanity save`) + } + async rollFear(html) { + this.rollDices("2d10+10", html, `system.stats.fear.value`,`Rolling for Fear save`) + } + async rollBody(html) { + this.rollDices("2d10+10", html, `system.stats.body.value`,`Rolling for Body save`) + } + async rollCredits(html) { + this.rollDices("2d10*10", html, `system.credits.value`,`Rolling for Credits`) + } + + async rollTable(html,id,tableId,type = "input"){ + + if (html.find(`img[id="` + id + `.value"]`).prop('hidden') == false && html.find(`img[id="system.class.value"]`).prop('value')!="" ) { + + let table = await fromUuid(tableId); + let tableResult = await table.draw({displayChat: true}); + + let tableRoll = tableResult.results[0].range[0]; + + let resultText = ""; + let resultUuid = []; + for (var i = 0; i < tableResult.results.length; i++) { + if (tableResult.results[i].type == "pack"){ + if(type=="ul"){ + this._element.find(`ul[id="${id}.text"]`).append(`
","").replaceAll("
","").replaceAll("","").replaceAll("
","").replaceAll("You must choose where to apply the bonus of (${obj.value})
`, + buttons: buttons_options, + default: "1", + //render: html => console.log("Register interactivity in the rendered dialog"), + //close: html => console.log("This always is logged no matter which option is chosen") + }); + d.render(true); + } + } + }catch{ + ui.notifications.error("Class has invalid stats and saves configuration."); + } + return; + } + + async _onDrop(event){ + await super._onDrop(event); + const droppedUuid = TextEditor.getDragEventData(event); + if (droppedUuid.type != "Item"){ + return; + } + await this.updateClass(droppedUuid.uuid); + //this._render(); + } + + activateListeners(html) { + super.activateListeners(html); + + /** Stats */ + html.find(`img[id="system.stats.strength.value"]`).click(ev => { + this.rollStrength(html) + }); + html.find(`img[id="system.stats.speed.value"]`).click(ev => { + this.rollSpeed(html) + }); + html.find(`img[id="system.stats.intellect.value"]`).click(ev => { + this.rollIntellect(html) + }); + html.find(`img[id="system.stats.combat.value"]`).click(ev => { + this.rollCombat(html) + }); + + /** health */ + html.find(`img[id="system.health.max"]`).click(ev => { + this.rollHealth(html) + }); + + /**Saves */ + html.find(`img[id="system.stats.sanity.value"]`).click(ev => { + this.rollSanity(html) + }); + html.find(`img[id="system.stats.fear.value"]`).click(ev => { + this.rollFear(html) + }); + html.find(`img[id="system.stats.body.value"]`).click(ev => { + this.rollBody(html) + }); + + /** credits */ + + html.find(`img[id="system.credits.value"]`).click(ev => { + this.rollCredits(html) + }); + /** Roll everything button */ + html.find(`div[id="roll.everything"]`).click(ev => { + this.rollEverything(html) + }); + + /** Class input + * when changed will lockup if its a defined class item and apply the apropiate modifiers + * + */ + html.find(`input[id="system.class.value"]`).change(ev => { + let class_name = html.find(`input[id="system.class.value"]`).prop('value'); + if(class_name == ""){ + //class name is empty, no class selected. + return; + } + let class_option = html.find(`option[class="class_option"][value="${class_name}"]`).prop('dataset'); + if(class_option == null){ + //class name is not part of the option, leave the string, and dont process uuid. + return; + } + //we have a valid class item to process. + let class_uuid = `Item.${class_option["uuid"]}`; + this.updateClass(class_uuid); + + }); + + + /** Roll patch */ + html.find(`img[id="system.class.patch.value"]`).click(ev => { + this.rollPatch(html) + }); + /** Roll trinket */ + html.find(`img[id="system.class.trinket.value"]`).click(ev => { + this.rollTrinket(html) + }); + /** Roll loadout */ + html.find(`img[id="system.class.loadout.value"]`).click(ev => { + this.rollLoadout(html) + }); + + + /** Save and submit */ + html.find(`div[id="submit"]`).click(ev => { + this.submit() + }); + } + + /** + * This method is called upon form submission after form data is validated + * @param event {Event} The initial triggering submission event + * @param formData {Object} The object of validated form data with which to update the object + * @private + */ + async _updateObject(event, formData) { + + let data = { + "system.credits.value": formData["system.credits.value"], + "system.stats.strength.value": formData["system.stats.strength.value"] + (formData["system.stats.strength.bonus"] || 0), + "system.stats.speed.value": formData["system.stats.speed.value"] + (formData["system.stats.speed.bonus"] || 0), + "system.stats.intellect.value": formData["system.stats.intellect.value"] + (formData["system.stats.intellect.bonus"] || 0), + "system.stats.combat.value": formData["system.stats.combat.value"] + (formData["system.stats.combat.bonus"] || 0), + "system.health.value": formData["system.health.max"] + (formData["system.health.mod"] || 0), + "system.health.max": formData["system.health.max"] + (formData["system.health.mod"] || 0), + "system.stats.sanity.value": formData["system.stats.sanity.value"] + (formData["system.stats.sanity.bonus"] || 0), + "system.stats.fear.value": formData["system.stats.fear.value"] + (formData["system.stats.fear.bonus"] || 0), + "system.stats.body.value": formData["system.stats.body.value"] + (formData["system.stats.body.bonus"] || 0), + } + if (formData["system.hits.bonus"]){ + data["system.hits.max"] = 2+formData["system.hits.bonus"]; + } + else{ + data["system.hits.max"] = 2; + } + + if (formData["name"]) { + data["name"] = formData["name"]; + } + if (formData["system.class.value"]) { + data["system.class.value"] = formData["system.class.value"]; + data["system.class.uuid"] = formData["system.class.uuid"]; + data["system.other.stressdesc.value"] = formData["system.class.traumaresponse"]; + } + ChatMessage.create({content:`Character Rolls