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 = $(`
${description}
`); + li.parents('.item-entry').append(div.hide()); + div.slideDown(200); } + li.toggleClass("expanded"); } /* -------------------------------------------- */ @@ -122,4 +151,14 @@ export class OseActorSheetMonster 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/scss/actor-base.scss b/src/scss/actor-base.scss index fb802e5..628191e 100644 --- a/src/scss/actor-base.scss +++ b/src/scss/actor-base.scss @@ -8,6 +8,31 @@ padding: 0; } + .panel { + border: 1px solid $colorDark; + .panel-title { + color: whitesmoke; + background: $colorDark; + line-height: 14px; + height: 20px; + text-align: center; + margin: 0; + padding: 4px; + display: flex; + h4 { + flex: 1; + } + .item-controls { + font-size: 12px; + flex: 0 0 18px; + } + } + .panel-content { + margin: 0; + padding: 0; + } + } + /* Header Summary Details */ .header-details { h1 { @@ -75,6 +100,7 @@ .attribute-name { color: whitesmoke; padding: 2px; + margin: 0; background: $colorDark; text-align: center; } @@ -85,6 +111,22 @@ } } } + .attribute-group { + .attributes { + .attribute { + display: flex; + flex-direction: row; + .attribute-name { + width: 40px; + margin: 0; + line-height: 28px; + } + } + } + } + .editor { + height: 300px; + } .inventory { .item-entry { padding: 0; @@ -93,10 +135,13 @@ .item { .item-image { flex: 0 0 24px; + height: 24px; background-size: cover; } .item-name { line-height: 24px; + height: 24px; + overflow: hidden; h4 { text-indent: 4px; margin: 0; @@ -123,13 +168,13 @@ } } } - &:nth-child(odd) { - background: rgba(0, 0, 0, 0.1); - } } .item-summary { font-size: 12px; - padding: 4px; + padding: 0 4px; + } + &:nth-child(odd) { + background: rgba(0, 0, 0, 0.1); } } } diff --git a/src/scss/character.scss b/src/scss/character.scss index 65313d5..bdb073b 100644 --- a/src/scss/character.scss +++ b/src/scss/character.scss @@ -18,40 +18,8 @@ /* Sheet Body */ /* ----------------------------------------- */ .sheet-body { - .attribute-group { - .attributes { - .attribute { - display: flex; - flex-direction: row; - .attribute-name { - width: 40px; - margin: 0; - line-height: 28px; - } - } - } - } - .editor { - height: 300px; - } } - .panel { - border: 1px solid $colorDark; - .panel-title { - color: whitesmoke; - background: $colorDark; - line-height: 14px; - height: 20px; - text-align: center; - margin: 0; - padding: 4px; - } - .panel-content { - margin: 0; - padding: 0; - } - } .abilities { .panel-content { diff --git a/src/scss/core.scss b/src/scss/core.scss index 15e1f92..d90a3f0 100644 --- a/src/scss/core.scss +++ b/src/scss/core.scss @@ -33,4 +33,8 @@ a:hover { text-shadow: none; } + + .resizable { + overflow: auto; + } } diff --git a/src/scss/monster.scss b/src/scss/monster.scss index e69de29..96cb216 100644 --- a/src/scss/monster.scss +++ b/src/scss/monster.scss @@ -0,0 +1,21 @@ +.ose.actor.monster { + min-height: 565px; + min-width: 460px; + .sheet-body { + .editor { + height: 300px; + } + } + .abilities { + .panel-content { + height: 230px; + overflow: auto; + } + } + .attribute-row { + padding: 2px; + .abilities { + margin: 2px; + } + } +} diff --git a/src/system.json b/src/system.json index 8f9b5b6..1a04213 100644 --- a/src/system.json +++ b/src/system.json @@ -2,7 +2,7 @@ "name": "ose", "title": "Old-School Essentials", "description": "Play OSR modules with Old-School Essentials on Foundry VTT", - "version": 0.1, + "version": 0.2, "minimumCoreVersion": "0.6.2", "compatibleCoreVersion": "0.6.4", "templateVersion": 2, diff --git a/src/template.json b/src/template.json index 869b76a..bf2f7e1 100644 --- a/src/template.json +++ b/src/template.json @@ -74,7 +74,7 @@ "value": 0, "mod": 0 }, - "thaco": { + "thac0": { "value": 19, "mod": 0 }, @@ -101,6 +101,8 @@ "alignment": "", "xp": 0, "treasure": "", + "size": "", + "appearing": "", "morale": 0 }, "saves": { @@ -110,7 +112,7 @@ "B": 10, "S": 10 }, - "thaco": { + "thac0": { "value": 19, "mod": 0 }, @@ -123,9 +125,35 @@ "value": 0, "mod": 0 }, + "attacks": { + "value": 1 + }, "movement": { "base": 0, "encounter": 0 + }, + "spells": { + "dc": 0, + "lvl1": { + "value": 0, + "max": 0 + }, + "lvl2": { + "value": 0, + "max": 0 + }, + "lvl3": { + "value": 0, + "max": 0 + }, + "lvl4": { + "value": 0, + "max": 0 + }, + "lvl5": { + "value": 0, + "max": 0 + } } } }, diff --git a/src/templates/actors/character-sheet.html b/src/templates/actors/character-sheet.html index f182d40..a878aa4 100644 --- a/src/templates/actors/character-sheet.html +++ b/src/templates/actors/character-sheet.html @@ -22,11 +22,7 @@ {{! Sheet Body }}
{{! Attributes Tab }} -
+
{{> "systems/ose/templates/actors/partials/character-attributes-tab.html"}}
@@ -40,4 +36,4 @@ button=true owner=owner editable=editable}}
- + \ No newline at end of file diff --git a/src/templates/actors/monster-sheet.html b/src/templates/actors/monster-sheet.html index e69de29..9786ce7 100644 --- a/src/templates/actors/monster-sheet.html +++ b/src/templates/actors/monster-sheet.html @@ -0,0 +1,33 @@ +
+ {{! Sheet Header }} +
+ {{> "systems/ose/templates/actors/partials/monster-header.html"}} +
+ + {{! Sheet Tab Navigation }} + + {{! Sheet Body }} +
+ {{! Attributes Tab }} +
+ {{> "systems/ose/templates/actors/partials/monster-attributes-tab.html"}} +
+
+ {{> "systems/ose/templates/actors/partials/character-spells-tab.html"}} +
+
+ {{editor content=data.details.biography target="data.details.biography" + button=true owner=owner editable=editable}} +
+
+
\ No newline at end of file diff --git a/src/templates/actors/partials/character-attributes-tab.html b/src/templates/actors/partials/character-attributes-tab.html index 860e1d6..c87817d 100644 --- a/src/templates/actors/partials/character-attributes-tab.html +++ b/src/templates/actors/partials/character-attributes-tab.html @@ -1,7 +1,7 @@