diff --git a/src/assets/heart_empty.png b/src/assets/heart_empty.png index 0673545..99578eb 100644 Binary files a/src/assets/heart_empty.png and b/src/assets/heart_empty.png differ diff --git a/src/assets/heart_full.png b/src/assets/heart_full.png index 29747b8..ed2da98 100644 Binary files a/src/assets/heart_full.png and b/src/assets/heart_full.png differ diff --git a/src/assets/shield.png b/src/assets/shield.png index a2f7eed..049f92a 100644 Binary files a/src/assets/shield.png and b/src/assets/shield.png differ diff --git a/src/lang/en.json b/src/lang/en.json index cd52679..fa0f5cd 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -57,8 +57,12 @@ "OSE.HealthShort": "HP", "OSE.HitDice": "Hit Dice", "OSE.HitDiceShort": "HD", - "OSE.Movement": "Movement", - "OSE.MovementShort": "MOV", + "OSE.Movement": "Movement Rate", + "OSE.MovementEncounter": "Encounter Movement Rate", + "OSE.MovementEncounterShort": "ENC", + "OSE.MovementOverland": "Overland Movement Rate", + "OSE.MovementOverlandShort": "OVE", + "OSE.MovementShort": "MR", "OSE.ArmorClass": "Armor Class", "OSE.ArmorClassShort": "AC", "OSE.AscArmorClassShort": "AAC", @@ -67,8 +71,10 @@ "OSE.Thac0": "THAC0", "OSE.MeleeShort": "MEL", "OSE.Melee": "Melee", + "OSE.MeleeBonus": "Melee Bonus", "OSE.MissileShort": "MIS", "OSE.Missile": "Missile", + "OSE.MissileBonus": "Missile Bonus", "OSE.Initiative": "Initiative", "OSE.InitiativeShort": "INIT", "OSE.Attacks": "Attacks Usable per Round", diff --git a/src/module/actor/actor-sheet.js b/src/module/actor/actor-sheet.js index a1417e5..d3790e9 100644 --- a/src/module/actor/actor-sheet.js +++ b/src/module/actor/actor-sheet.js @@ -85,6 +85,13 @@ export class OseActorSheet extends ActorSheet { }); }); + html.find(".item-image").click(async (ev) => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.actor.getOwnedItem(li.data("itemId")); + item.roll(); + }); + + super.activateListeners(html); } diff --git a/src/module/helpers.js b/src/module/helpers.js index 0062abd..3613183 100644 --- a/src/module/helpers.js +++ b/src/module/helpers.js @@ -12,6 +12,14 @@ export const registerHelpers = async function () { return parseInt(rh) - parseInt(lh); }); + Handlebars.registerHelper("divide", function (lh, rh) { + return Math.floor(parseFloat(lh) / parseFloat(rh)); + }); + + Handlebars.registerHelper("mult", function (lh, rh) { + return parseInt(lh) * parseInt(rh); + }); + Handlebars.registerHelper("counter", function (status, value, max) { return status ? Math.clamped((100.0 * value) / max, 0, 100) : Math.clamped(100 - (100.0 * value) / max, 0, 100); }); diff --git a/src/module/item/entity.js b/src/module/item/entity.js index 0c23cc0..8c979a3 100644 --- a/src/module/item/entity.js +++ b/src/module/item/entity.js @@ -12,4 +12,79 @@ export class OseItem extends Item { prepareData() { super.prepareData(); } + + getChatData(htmlOptions) { + const data = duplicate(this.data.data); + + // Rich text description + data.description = TextEditor.enrichHTML(data.description, htmlOptions); + + // Item properties + const props = []; + const labels = this.labels; + + if (this.data.type == "weapon") { + props.push(data.qualities); + } + if (this.data.type == "spell") { + props.push( + `${data.class} ${data.lvl}`, + data.range, + data.duration + ); + } + if (data.hasOwnProperty("equipped")) { + props.push(data.equipped ? "Equipped" : "Not Equipped"); + } + + // Filter properties and return + data.properties = props.filter((p) => !!p); + return data; + } + + /** + * Roll the item to Chat, creating a chat card which contains follow up attack or damage roll options + * @return {Promise} + */ + async roll({ configureDialog = true } = {}) { + // 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, + data: this.getChatData(), + labels: this.labels, + hasAttack: this.hasAttack, + isHealing: this.isHealing, + hasDamage: this.hasDamage, + isSpell: this.data.type === "spell", + hasSave: this.hasSave, + }; + + // Render the chat card template + const template = `systems/ose/templates/chat/item-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.getWhisperRecipients("GM"); + if (rollMode === "blindroll") chatData["blind"] = true; + + // Create the chat message + return ChatMessage.create(chatData); + } } diff --git a/src/scss/actor-base.scss b/src/scss/actor-base.scss index 2099b9c..882d1e7 100644 --- a/src/scss/actor-base.scss +++ b/src/scss/actor-base.scss @@ -95,7 +95,7 @@ padding: 0; .attribute { position: relative; - margin: 10px; + margin: 8px; border: 1px solid $colorTan; .attribute-name { color: whitesmoke; @@ -104,25 +104,31 @@ background: $colorDark; text-align: center; } - .attribute-value { - padding: 4px; - display: flex; - flex-direction: row; - &.multiple input { - min-width: 28px; + &.attribute-secondaries { + margin: 10px 5px; + } + &.ability-score { + height: 40px; + .attribute-value { + line-height: 36px; } } + .attribute-value { + text-align: center; + padding: 4px; + } .attribute-mod { position: absolute; color: $colorTan; right: 5px; - top: 0; + top: -5px; font-size: 13px; } } } .attribute-group { flex: 0 0 105px; + margin: auto 0; .attributes { .attribute { display: flex; @@ -130,7 +136,7 @@ .attribute-name { width: 40px; margin: 0; - line-height: 28px; + line-height: 38px; a { margin: auto; } diff --git a/src/scss/apps.scss b/src/scss/apps.scss index e69de29..fcd39d7 100644 --- a/src/scss/apps.scss +++ b/src/scss/apps.scss @@ -0,0 +1,97 @@ + +.ose.chat-card { + font-style: normal; + font-size: 12px; + + .card-header { + padding: 3px 0; + border-top: 2px groove #FFF; + border-bottom: 2px groove #FFF; + + img { + flex: 0 0 36px; + margin-right: 5px; + } + + h3 { + flex: 1; + margin: 0; + line-height: 36px; + color: $colorOlive; + &:hover { + color: #111; + text-shadow: 0 0 10px red; + } + } + } + + .card-content { + margin: 5px 0; + + h3 { + font-size: 12px; + margin: 0; + font-weight: bold; + } + + > * { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + } + } + + .card-buttons { + margin: 5px 0; + + span { + display: block; + line-height: 28px; + text-align: center; + border: 1px solid $colorTan; + } + + button { + font-size: 12px; + height: 24px; + line-height: 20px; + margin: 2px 0; + } + } + + .card-footer { + padding: 3px 0 0; + border-top: 2px groove #FFF; + + span { + border-right: 2px groove #FFF; + padding: 0 5px 0 0; + font-size: 10px; + + &:last-child { + border-right: none; + padding-right: 0; + } + } + } + } + + .dice-roll .dice-total { + &.success { + color: inherit; + background: #c7d0c0; + border: 1px solid #006c00; + } + &.failure { + color: inherit; + background: #ffdddd; + border: 1px solid #6e0000; + } + &.critical { + color: green; + } + &.fumble { + color: red; + } + } \ No newline at end of file diff --git a/src/scss/character.scss b/src/scss/character.scss index 6576274..56f7748 100644 --- a/src/scss/character.scss +++ b/src/scss/character.scss @@ -28,10 +28,10 @@ .health { &.armor-class { background: url('/systems/ose/assets/shield.png') no-repeat center; - background-size: 90px; + background-size: 70px; } margin: 10px 0; - height: 90px; + height: 70px; position: relative; input { font-size: 16px; @@ -42,25 +42,25 @@ border-bottom: none; position: absolute; font-size: 24px; - top: 28px; + top: 10px; width: 70px; left: calc(50% - 35px); } .health-bottom { border-bottom: none; position: absolute; - bottom: 12px; + bottom: 8px; width: 40px; right: calc(50% + -20px); } .health-empty { background: url('/systems/ose/assets/heart_empty.png') no-repeat center; - background-size: 90px; + background-size: 70px; background-position: top; } .health-full { background: url('/systems/ose/assets/heart_full.png') no-repeat center; - background-size: 90px; + background-size: 70px; background-position: bottom; } } diff --git a/src/template.json b/src/template.json index a0ebbc9..84ae158 100644 --- a/src/template.json +++ b/src/template.json @@ -24,7 +24,10 @@ }, "thac0": { "value": 19, - "mod": 0 + "mod": { + "missile": 0, + "melee": 0 + } }, "saves": { "death": 10, @@ -34,8 +37,7 @@ "spell": 10 }, "movement": { - "base": 0, - "encounter": 0 + "base": 120 } }, "spellcaster": { diff --git a/src/templates/actors/dialogs/tweaks-dialog.html b/src/templates/actors/dialogs/tweaks-dialog.html index 964c834..42fff28 100644 --- a/src/templates/actors/dialogs/tweaks-dialog.html +++ b/src/templates/actors/dialogs/tweaks-dialog.html @@ -35,6 +35,28 @@ /> +