diff --git a/README.md b/README.md index dfbda13..fa01ccf 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -### Old School Essentials System for Foundry VTT \ No newline at end of file +# Old School Essentials System for Foundry VTT + +## License +Old-School Essentials is a trademark of Necrotic Gnome. +This Foundry VTT system requires Old-School Essentials Core Rules and does not contain any copyrighted material. + +## Contributions +This system is currently under heavy development. +Feel free to grab a TO DO issue from the gitlab board. You can then do a merge request on the `development` branch. \ No newline at end of file diff --git a/src/lang/en.json b/src/lang/en.json index c79392d..7089dbe 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1,6 +1,7 @@ { "OSE.Edit": "Edit", "OSE.Delete": "Delete", + "OSE.Add": "Add", "OSE.Name": "Name", "OSE.Class": "Class", @@ -8,6 +9,10 @@ "OSE.Alignment": "Alignment", "OSE.Level": "Level", "OSE.Experience": "Experience", + "OSE.Treasure": "Treasure type", + "OSE.Size": "Size", + "OSE.Morale": "Morale", + "OSE.Appearing": "NA", "OSE.scores.str.long": "Strength", "OSE.scores.str.short": "STR", @@ -23,18 +28,31 @@ "OSE.scores.cha.short": "CHA", "OSE.saves.death.short": "D", + "OSE.saves.death.long": "Death", "OSE.saves.wands.short": "W", + "OSE.saves.wands.long": "Wands", "OSE.saves.paralysis.short": "P", + "OSE.saves.paralysis.long": "Paralysis", "OSE.saves.breath.short": "B", + "OSE.saves.breath.long": "Breath", "OSE.saves.spells.short": "S", + "OSE.saves.spells.long": "Spells", "OSE.Health": "Hit Points", + "OSE.HealthShort": "HP", "OSE.HitDice": "Hit Dice", + "OSE.HitDiceShort": "HD", "OSE.Movement": "Movement", + "OSE.MovementShort": "MOV", "OSE.SpecialMovement": "Special Movement", "OSE.ArmorClass": "Armor Class", + "OSE.ArmorClassShort": "AC", "OSE.SpellDC": "DC", + "OSE.Thac0": "THAC0", "OSE.Initiative": "Initiative", + "OSE.InitiativeShort": "INIT", + "OSE.Attacks": "Attacks Usable per Round", + "OSE.AttacksShort": "ATT", "OSE.category.attributes": "Attributes", @@ -42,5 +60,6 @@ "OSE.category.spells": "Spells", "OSE.category.notes": "Notes", - "OSE.panel.abilities": "Abilities" + "OSE.panel.abilities": "Abilities", + "OSE.panel.equipment": "Equipment" } \ No newline at end of file diff --git a/src/lang/fr.json b/src/lang/fr.json index 7a73a41..1ff3656 100644 --- a/src/lang/fr.json +++ b/src/lang/fr.json @@ -1,2 +1,65 @@ { + "OSE.Edit": "Modifier", + "OSE.Delete": "Supprimer", + "OSE.Add": "Ajouter", + + "OSE.Name": "Nom", + "OSE.Class": "Classe", + "OSE.Title": "Titre", + "OSE.Alignment": "Alignement", + "OSE.Level": "Niveau", + "OSE.Experience": "Expérience", + "OSE.Treasure": "Butin", + "OSE.Size": "Taille", + "OSE.Morale": "Moral", + "OSE.Appearing": "NA", + + "OSE.scores.str.long": "Force", + "OSE.scores.str.short": "FOR", + "OSE.scores.wis.long": "Sagesse", + "OSE.scores.wis.short": "SAG", + "OSE.scores.int.long": "Intelligence", + "OSE.scores.int.short": "INT", + "OSE.scores.dex.long": "Dextérité", + "OSE.scores.dex.short": "DEX", + "OSE.scores.con.long": "Constitution", + "OSE.scores.con.short": "CON", + "OSE.scores.cha.long": "Charisme", + "OSE.scores.cha.short": "CHA", + + "OSE.saves.death.short": "D", + "OSE.saves.death.long": "Mort", + "OSE.saves.wands.short": "W", + "OSE.saves.wands.long": "Baguettes", + "OSE.saves.paralysis.short": "P", + "OSE.saves.paralysis.long": "Paralysie", + "OSE.saves.breath.short": "B", + "OSE.saves.breath.long": "Souffle", + "OSE.saves.spells.short": "S", + "OSE.saves.spells.long": "Sorts", + + "OSE.Health": "Points de Vie", + "OSE.HealthShort": "PV", + "OSE.HitDice": "Dés de Vie", + "OSE.HitDiceShort": "DV", + "OSE.Movement": "Mouvement", + "OSE.MovementShort": "MOUV", + "OSE.SpecialMovement": "Mouvement Spécial", + "OSE.ArmorClass": "Classe d'Armure", + "OSE.ArmorClassShort": "CA", + "OSE.SpellDC": "DF", + "OSE.Thac0": "THAC0", + "OSE.Initiative": "Initiative", + "OSE.InitiativeShort": "INIT", + "OSE.Attacks": "Attaques par Round", + "OSE.AttacksShort": "ATT", + + + "OSE.category.attributes": "Attributs", + "OSE.category.inventory": "Inventaire", + "OSE.category.spells": "Sorts", + "OSE.category.notes": "Notes", + + "OSE.panel.abilities": "Aptitudes", + "OSE.panel.equipment": "Equipement" } \ No newline at end of file diff --git a/src/module/actor/character-sheet.js b/src/module/actor/character-sheet.js index 38aefb6..f99fa3b 100644 --- a/src/module/actor/character-sheet.js +++ b/src/module/actor/character-sheet.js @@ -33,6 +33,20 @@ export class OseActorSheetCharacter extends ActorSheet { /* -------------------------------------------- */ + // Override to set resizable initial size + async _renderInner(...args) { + const html = await super._renderInner(...args); + this.form = html[0]; + + // Resize resizable classes + let resizable = html.find('.resizable'); + resizable.each((_, el) => { + let heightDelta = this.position.height - (this.options.height); + el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; + }); + return html; + } + /** * Prepare data for rendering the Actor sheet * The prepared data object contains both the actor data as well as additional sheet options @@ -94,12 +108,6 @@ export class OseActorSheetCharacter extends ActorSheet { li.toggleClass("expanded"); } - _onRollAttribute(event) { - event.preventDefault(); - let attribute = event.currentTarget.dataset.attribute; - this.actor.rollAttribute(attribute, { event: event }); - } - /** * Activate event listeners using the prepared sheet HTML * @param html {HTML} The prepared HTML object ready to be rendered into the DOM @@ -141,4 +149,14 @@ export class OseActorSheetCharacter extends ActorSheet { // Handle default listeners last so system listeners are triggered first super.activateListeners(html); } + + async _onResize(event) { + super._onResize(event); + let html = $(event.path); + let resizable = html.find('.resizable'); + resizable.each((_, el) => { + let heightDelta = this.position.height - (this.options.height); + el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; + }); + } } diff --git a/src/module/actor/entity.js b/src/module/actor/entity.js index 2489c99..42d55c8 100644 --- a/src/module/actor/entity.js +++ b/src/module/actor/entity.js @@ -17,65 +17,4 @@ export class OseActor extends Actor { /* -------------------------------------------- */ /* Rolls */ /* -------------------------------------------- */ - - rollAttribute(attributeId, options = {}) { - const label = CONFIG.MAJI.attributes[attributeId]; - - const abl = this.data.data.attributes[attributeId]; - let parts = []; - if (abl.value <= 4) { - parts.push("2d4"); - } else if (abl.value <= 7) { - parts.push("2d6"); - } else { - parts.push("2d8"); - } - let rollMode = game.settings.get("core", "rollMode"); - let roll = new Roll(parts.join(" + "), {}).roll(); - roll.toMessage( - { - speaker: ChatMessage.getSpeaker({ actor: this }), - flavor: `${label} Attribute Test`, - }, - { rollMode } - ); - return roll; - } - - rollInit(monsterId, options = {}) { - let monster = game.actors.get(monsterId); - let speed = monster.data.data.attributes.speed.value + monster.data.data.attributes.speed.mod; - if (!game.combats.active) return; - let combatant = game.combats.active.getCombatant(this.actor); - console.log(combatant); - } - - static async applyDamage(roll, options = {}) { - let value = Math.floor(parseFloat(roll.find(".dice-total").text())); - const promises = []; - for (let t of canvas.tokens.controlled) { - let a = t.actor, - hp = a.data.data.hp; - let delta = 0; - if (a.data.type == "monster") { - if (options.vulnerable) { - delta -= value + a.data.data.affinity.value; - } else if (options.resistant) { - delta -= Math.max(0, value - a.data.data.resistance.value); - } else if (options.healing) { - delta += value; - } else { - delta -= value; - } - } else { - delta -= options.healing ? -value : value; - } - promises.push( - t.actor.update({ - "data.hp.value": Math.clamped(hp.value + delta, 0, hp.max), - }) - ); - } - return Promise.all(promises); - } } diff --git a/src/module/actor/monster-sheet.js b/src/module/actor/monster-sheet.js index d4f08a9..4918b4a 100644 --- a/src/module/actor/monster-sheet.js +++ b/src/module/actor/monster-sheet.js @@ -18,14 +18,14 @@ export class OseActorSheetMonster extends ActorSheet { return mergeObject(super.defaultOptions, { classes: ["ose", "sheet", "monster", "actor"], template: "systems/ose/templates/actors/monster-sheet.html", - width: 520, - height: 580, - resizable: false, + width: 450, + height: 560, + resizable: true, tabs: [ { navSelector: ".tabs", contentSelector: ".sheet-body", - initial: "notes", + initial: "attributes", }, ], }); @@ -33,16 +33,36 @@ export class OseActorSheetMonster extends ActorSheet { /* -------------------------------------------- */ + // Override to set resizable initial size + async _renderInner(...args) { + const html = await super._renderInner(...args); + this.form = html[0]; + + // Resize resizable classes + let resizable = html.find('.resizable'); + if (resizable.length == 0) { + return; + } + resizable.each((_, el) => { + let heightDelta = this.position.height - (this.options.height); + el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`; + }); + return html; + } + /** * Prepare data for rendering the Actor sheet * The prepared data object contains both the actor data as well as additional sheet options */ getData() { const data = super.getData(); + data.config = CONFIG.OSE; // Prepare owned items this._prepareItems(data); + + // DEBUG return data; } @@ -51,31 +71,40 @@ export class OseActorSheetMonster extends ActorSheet { * @private */ _prepareItems(data) { + // Partition items by category + let [inventory, abilities, spells] = data.items.reduce( + (arr, item) => { + // Classify items into types + if (item.type === "item") arr[0].push(item); + if (item.type === "ability") arr[1].push(item); + else if (item.type === "spell") arr[2].push(item); + return arr; + }, + [[], [], [], []] + ); + + // Assign and return + data.inventory = inventory; + data.spells = spells; + data.abilities = abilities; } + _onItemSummary(event) { event.preventDefault(); - let li = $(event.currentTarget).parents(".item-entry"), - expanded = !li.children(".collapsible").hasClass("collapsed"); - li = $(li); - let ol = li.children(".collapsible"); - let icon = li.find("i.fas"); - - // Collapse the Playlist - if (expanded) { - ol.slideUp(200, () => { - ol.addClass("collapsed"); - icon.removeClass("fa-angle-up").addClass("fa-angle-down"); - }); - } - - // Expand the Playlist - else { - ol.slideDown(200, () => { - ol.removeClass("collapsed"); - icon.removeClass("fa-angle-down").addClass("fa-angle-up"); - }); + let li = $(event.currentTarget).parents(".item"), + item = this.actor.getOwnedItem(li.data("item-id")), + description = TextEditor.enrichHTML(item.data.data.description); + // Toggle summary + if ( li.hasClass("expanded") ) { + let summary = li.parents('.item-entry').children(".item-summary"); + summary.slideUp(200, () => summary.remove()); + } else { + let div = $(`