ENH: Monster sheet improv

master
U~man 2020-07-03 17:13:53 +02:00
parent 5afadf6039
commit d9ec802338
14 changed files with 263 additions and 302 deletions

View File

@ -22,6 +22,7 @@
"OSE.Morale": "Morale",
"OSE.Retainer": "Retainer",
"OSE.Appearing": "NA",
"OSE.Attack": "Attack",
"OSE.Loyalty": "Loyalty Rating",
"OSE.LoyaltyShort": "LR",

View File

@ -26,7 +26,7 @@ export class OseActorSheet extends ActorSheet {
*/
_prepareItems(data) {
// Partition items by category
let [inventory, weapons, armors, abilities, spells] = data.items.reduce(
let [items, weapons, armors, abilities, spells] = data.items.reduce(
(arr, item) => {
// Classify items into types
if (item.type === "item") arr[0].push(item);
@ -42,19 +42,44 @@ export class OseActorSheet extends ActorSheet {
// Sort spells by level
var sortedSpells = {};
for (var i = 0; i < spells.length; i++) {
let lvl = spells[i].data.lvl
let lvl = spells[i].data.lvl;
if (!sortedSpells[lvl]) sortedSpells[lvl] = [];
sortedSpells[lvl].push(spells[i]);
}
// Assign and return
data.inventory = inventory;
data.weapons = weapons;
data.armors = armors;
data.spells = sortedSpells;
data.owned = {
items: items,
weapons: weapons,
armors: armors,
};
data.abilities = abilities;
data.spells = sortedSpells;
}
_onItemSummary(event) {
event.preventDefault();
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 = $(`<div class="item-summary">${description}</div>`);
li.parents(".item-entry").append(div.hide());
div.slideDown(200);
}
li.toggleClass("expanded");
}
activateListeners(html) {
// Item summaries
html
.find(".item .item-name h4")
.click((event) => this._onItemSummary(event));
html.find(".saving-throw .attribute-name a").click((ev) => {
let actorObject = this.actor;
let element = event.currentTarget;
@ -91,7 +116,6 @@ export class OseActorSheet extends ActorSheet {
item.roll();
});
super.activateListeners(html);
}

View File

@ -57,23 +57,6 @@ export class OseActorSheetCharacter extends OseActorSheet {
/* -------------------------------------------- */
_onItemSummary(event) {
event.preventDefault();
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 = $(`<div class="item-summary">${description}</div>`);
li.parents(".item-entry").append(div.hide());
div.slideDown(200);
}
li.toggleClass("expanded");
}
async _onQtChange(event) {
event.preventDefault();
const itemId = event.currentTarget.closest(".item").dataset.itemId;
@ -133,11 +116,6 @@ export class OseActorSheetCharacter extends OseActorSheet {
.click((ev) => ev.target.select())
.change(this._onQtChange.bind(this));
// Item summaries
html
.find(".item .item-name h4")
.click((event) => this._onItemSummary(event));
html.find(".ability-score .attribute-name a").click((ev) => {
let actorObject = this.actor;
let element = event.currentTarget;
@ -145,6 +123,13 @@ export class OseActorSheetCharacter extends OseActorSheet {
actorObject.rollCheck(score, { event: event });
});
html.find(".attack a").click(ev => {
let actorObject = this.actor;
let element = event.currentTarget;
let attack = element.parentElement.parentElement.dataset.attack;
actorObject.rollAttack(attack, { event: event });
})
// Handle default listeners last so system listeners are triggered first
super.activateListeners(html);
}

View File

@ -42,6 +42,21 @@ export class OseActor extends Actor {
});
}
rollAttack(attack, options={}) {
const label = game.i18n.localize(`OSE.${attack}`);
const rollParts = ['1d20'];
// Roll and return
return OseDice.Roll({
event: options.event,
parts: rollParts,
data: this.data,
speaker: ChatMessage.getSpeaker({ actor: this }),
flavor: `${label} ${game.i18n.localize('OSE.Attack')}`,
title: `${label} ${game.i18n.localize('OSE.Attack')}`,
});
}
computeModifiers() {
let _valueToMod = (val) => {
switch (val) {

View File

@ -45,25 +45,42 @@ export class OseActorSheetMonster extends OseActorSheet {
return data;
}
_onItemSummary(event) {
event.preventDefault();
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 = $(`<div class="item-summary">${description}</div>`);
li.parents('.item-entry').append(div.hide());
div.slideDown(200);
}
li.toggleClass("expanded");
}
/* -------------------------------------------- */
async _chooseItemType(
choices = ['weapon', 'armor', 'shield', 'gear'],
) {
let templateData = { upper: '', lower: '', types: choices },
dlg = await renderTemplate(
'templates/sidebar/entity-create.html',
templateData,
);
//Create Dialog window
return new Promise((resolve) => {
new Dialog({
title: '',
content: dlg,
buttons: {
ok: {
label: game.i18n.localize('OSE.Ok'),
icon: '<i class="fas fa-check"></i>',
callback: (html) => {
resolve({
type: html.find('select[name="type"]').val(),
name: html.find('input[name="name"]').val(),
});
},
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize('OSE.Cancel'),
},
},
default: 'ok',
}).render(true);
});
}
/**
* Activate event listeners using the prepared sheet HTML
* @param html {HTML} The prepared HTML object ready to be rendered into the DOM
@ -90,17 +107,32 @@ export class OseActorSheetMonster extends OseActorSheet {
event.preventDefault();
const header = event.currentTarget;
const type = header.dataset.type;
const itemData = {
name: `New ${type.capitalize()}`,
type: type,
data: duplicate(header.dataset),
};
delete itemData.data["type"];
return this.actor.createOwnedItem(itemData);
});
html.find(".item-name").click((event) => {
this._onItemSummary(event);
// item creation helper func
let createItem = function (
type,
name = `New ${type.capitalize()}`,
) {
const itemData = {
name: name ? name : `New ${type.capitalize()}`,
type: type,
data: duplicate(header.dataset),
};
delete itemData.data['type'];
return itemData;
};
// Getting back to main logic
if (type == 'choice') {
const choices = header.dataset.choices.split(',');
this._chooseItemType(choices).then((dialogInput) => {
const itemData = createItem(dialogInput.type, dialogInput.name);
this.actor.createOwnedItem(itemData, {});
});
return;
}
const itemData = createItem(type);
return this.actor.createOwnedItem(itemData, {});
});
// Handle default listeners last so system listeners are triggered first

View File

@ -55,7 +55,6 @@ export class OseItem extends Item {
item: this.data,
data: this.getChatData(),
labels: this.labels,
hasAttack: this.hasAttack,
isHealing: this.isHealing,
hasDamage: this.hasDamage,
isSpell: this.data.type === "spell",

View File

@ -32,7 +32,7 @@
padding: 0;
}
}
/* Header Summary Details */
.header-details {
h1 {
@ -84,7 +84,7 @@
padding: 2px 10px 0;
margin-left: -5px;
text-indent: 4px;
background: url('/ui/parchment.jpg');
background: url("/ui/parchment.jpg");
border-top-right-radius: 4px;
border-top-left-radius: 80px;
box-shadow: 0 0 6px 1px $colorDark;
@ -163,6 +163,127 @@
}
}
}
.inventory {
overflow: auto;
.header-spells {
line-height: 30px;
}
.item-titles {
text-align: center;
padding-top: 4px;
.item-name {
text-align: left;
text-indent: 8px;
}
font-weight: 300;
font-size: 12px;
background: $colorDark;
color: white;
input {
color: white;
margin: auto;
}
}
.item-list {
list-style: none;
margin: 0;
padding: 0;
& > * {
line-height: 30px;
}
.item-summary {
font-size: 13px;
padding: 0 4px;
line-height: 20px;
}
.item-header {
@extend %header-field !optional;
padding: 0px;
margin-bottom: 0px;
}
.item-entry {
&:nth-child(even) {
.item {
background: rgba(0, 0, 0, 0.1);
}
}
}
.item {
line-height: 30px;
height: 30px;
overflow: hidden;
}
.item-equipped {
grid-area: item-equipped;
justify-self: center;
}
.item-name {
text-indent: 8px;
text-align: left;
overflow: hidden;
height: 30px;
margin: 0;
line-height: 30px;
&:hover .item-image {
background-image: url("/icons/svg/d20-grey.svg") !important;
cursor: pointer;
}
.item-image {
flex-basis: 30px;
flex-grow: 0;
background-size: contain;
background-repeat: no-repeat;
&:hover {
background-image: url("/icons/svg/d20-black.svg") !important;
}
}
h4 {
margin: 0;
}
}
}
.field-longer {
text-indent: 8px;
text-align: left;
flex-basis: 150px;
font-size: 12px;
flex-grow: 0;
}
.field-long {
flex-basis: 65px;
flex-grow: 0;
text-align: center;
font-size: 12px;
}
.field-short {
font-size: 12px;
flex-basis: 45px;
flex-grow: 0;
text-align: center;
&.quantity {
display: flex;
input {
margin: 3px 0;
border-bottom: none;
}
}
}
.item-controls {
font-size: 12px;
flex-basis: 60px;
flex-grow: 0;
text-align: right;
margin-right: 4px;
.item-unequipped {
color: rgba(0, 0, 0, 0.2);
}
}
&.spells {
.item-controls {
flex-basis: 30px;
}
}
}
.editor {
height: 300px;
padding: 4px;

View File

@ -64,135 +64,6 @@
background-position: bottom;
}
}
.inventory {
overflow: auto;
.header-spells {
line-height: 30px;
}
.item-titles {
text-align: center;
margin-top: 1px;
padding-top: 2px;
.item-name {
text-align: left;
text-indent: 8px;
}
font-weight: 300;
font-size: 12px;
background: $colorDark;
color: white;
input {
color: white;
margin: auto;
}
}
.item-list {
list-style: none;
margin: 0;
padding: 0;
&>* {
line-height: 30px;
}
.item-summary {
font-size: 13px;
padding: 0 4px;
line-height: 20px;
}
.item-header {
@extend %header-field !optional;
padding: 0px;
margin-bottom: 0px;
}
.item-entry {
&:nth-child(even) {
.item {
background: rgba(0, 0, 0, 0.1);
}
}
}
.item {
line-height: 30px;
height: 30px;
overflow: hidden;
}
.item-equipped {
grid-area: item-equipped;
justify-self: center;
}
.item-name {
text-indent: 8px;
text-align: left;
overflow: hidden;
height: 30px;
margin: 0;
line-height: 30px;
&:hover .item-image {
background-image: url("/icons/svg/d20-grey.svg") !important;
cursor: pointer;
}
.item-image {
flex-basis: 30px;
flex-grow: 0;
background-size: contain;
background-repeat: no-repeat;
&:hover {
background-image: url("/icons/svg/d20-black.svg") !important;
}
}
h4 {
margin: 0;
}
}
}
.field-longer {
text-indent: 8px;
text-align: left;
flex-basis: 150px;
font-size: 12px;
flex-grow: 0;
}
.field-long {
flex-basis: 65px;
flex-grow: 0;
text-align: center;
font-size: 12px;
}
.field-short {
font-size: 12px;
flex-basis: 45px;
flex-grow: 0;
text-align: center;
&.quantity {
display: flex;
input {
margin: 3px 0;
border-bottom: none;
}
}
}
.item-controls {
font-size: 12px;
flex-basis: 60px;
flex-grow: 0;
text-align: right;
margin-right: 4px;
.item-unequipped {
color: rgba(0, 0, 0, 0.2);
}
}
&.spells {
.item-controls {
flex-basis: 30px;
}
}
}
}
/* ----------------------------------------- */
/* Inventory */
/* ----------------------------------------- */
.inventory {
}
/* ----------------------------------------- */
/* Item Controls */

View File

@ -6,68 +6,4 @@
height: 300px;
}
}
.abilities {
.panel-content {
height: 230px;
overflow: auto;
}
}
.attribute-row {
padding: 2px;
.abilities {
margin: 2px;
}
}
.inventory {
.item-entry {
padding: 0;
margin: 0;
list-style: none;
.item {
.item-image {
flex: 0 0 30px;
height: 30px;
background-size: cover;
}
.item-name {
line-height: 30px;
height: 30px;
overflow: hidden;
h4 {
text-indent: 4px;
margin: 0;
cursor: pointer;
&:hover {
color: whitesmoke;
background: linear-gradient(
45deg,
rgba(0, 0, 0, 0.5),
transparent
);
}
}
}
.item-controls {
line-height: 30px;
flex: 0 0 32px;
margin: 0 3px;
.fas {
color: $colorTan;
font-size: 12px;
&:hover {
color: $colorDark;
}
}
}
}
.item-summary {
font-size: 13px;
padding: 0 4px;
}
&:nth-child(odd) {
background: rgba(0, 0, 0, 0.1);
}
}
}
}

View File

@ -43,7 +43,6 @@
"spellcaster": {
"spells": {
"enabled": false,
"dc": 0,
"1": {
"value": 0,
"max": 0

View File

@ -113,8 +113,8 @@
</div>
<div class="flexrow">
<ul class="attributes flexrow">
<li class="attribute attribute-secondaries">
<h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}">{{localize 'OSE.MeleeShort'}}</h4>
<li class="attribute attribute-secondaries attack" data-attack="Melee">
<h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}"><a>{{localize 'OSE.MeleeShort'}}</a></h4>
<div class="flexrow">
<div class="attribute-value">
{{subtract data.thac0.mod.melee (subtract mods.str data.thac0.value)}}
@ -131,8 +131,8 @@
</div>
</div>
</li>
<li class="attribute attribute-secondaries">
<h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}">{{localize 'OSE.MissileShort'}}</h4>
<li class="attribute attribute-secondaries attack" data-attack="Missile">
<h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}"><a>{{localize 'OSE.MissileShort'}}</a></h4>
<div class="flexrow">
<div class="attribute-value">
{{subtract data.thac0.mod.missile (subtract mods.dex data.thac0.value)}}

View File

@ -12,7 +12,7 @@
</div>
</li>
<ol class="item-list">
{{#each weapons as |item|}}
{{#each owned.weapons as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
@ -63,7 +63,7 @@
</div>
</li>
<ol class="item-list">
{{#each armors as |item|}}
{{#each owned.armors as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
@ -109,7 +109,7 @@
</div>
</li>
<ol class="item-list">
{{#each inventory as |item|}}
{{#each owned.items as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">

View File

@ -1,12 +1,4 @@
<section class="inventory spells resizable" data-base-size="320">
<div>
<li class="flexrow header-spells">
<div class="item-name"></div>
<div class="field-short">{{localize 'OSE.SpellDC'}}</div>
<div class="field-short"><input type="text" value="{{data.spells.dc}}" name="data.spells.dc" data-dtype="Number"
placeholder="0"></div>
</li>
</div>
{{#each spells as |spellGroup id|}}
<ol class="item-list">
<li class="item-titles flexrow">

View File

@ -62,18 +62,18 @@
</section>
<section class="flexrow attribute-row">
{{!-- Skills and abilities --}}
<div class="flex3 panel abilities">
<div class="panel-title">
<h4>{{localize 'OSE.panel.abilities'}}</h4>
<div class="item-controls">
<div class="flex3 panel inventory abilities">
<div>
<li class="item-titles flexrow panel-title">
<div class="item-name">{{localize 'OSE.panel.abilities'}} & {{localize 'OSE.panel.equipment'}}</div>
<div class="item-controls">
{{#if owner}}
<a class="item-control item-create" title='{{localize "OSE.Add"}}' data-type="ability"><i
class="fas fa-plus"></i></a>
<a class="item-control item-create" title='{{localize "OSE.Add"}}' data-type="choice"
data-choices="weapon,ability,armor,item"><i class="fas fa-plus"></i></a>
{{/if}}
</div>
</div>
<ul class="panel-content inventory resizable" data-base-size="220">
<div class="">
</div>
</li>
<ol class="item-list resizable" data-base-size="240">
{{#each abilities as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
@ -85,32 +85,17 @@
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "Ose.Edit"}}'><i
<a class="item-control item-edit" title='{{localize "OSE.Edit"}}'><i
class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "Ose.Delete"}}'><i
<a class="item-control item-delete" title='{{localize "OSE.Delete"}}'><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</div>
</ul>
</div>
{{!-- Equipment --}}
<div class="flex3 panel abilities">
<div class="panel-title">
<h4>{{localize 'OSE.panel.equipment'}}</h4>
<div class="item-controls">
{{#if owner}}
<a class="item-control item-create" title='{{localize "OSE.Add"}}' data-type="item"><i
class="fas fa-plus"></i></a>
{{/if}}
</div>
</div>
<ul class="panel-content inventory resizable" data-base-size="220">
<div class="">
{{#each inventory as |item|}}
{{#each owned as |section| }}
{{#each section as |item|}}
<li class="item-entry">
<div class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name flexrow">
@ -120,18 +105,19 @@
</h4>
</div>
<div class="item-controls">
{{#if ../owner}}
<a class="item-control item-edit" title='{{localize "Ose.Edit"}}'><i
{{#if ../../owner}}
<a class="item-control item-edit" title='{{localize "OSE.Edit"}}'><i
class="fas fa-edit"></i></a>
<a class="item-control item-delete" title='{{localize "Ose.Delete"}}'><i
<a class="item-control item-delete" title='{{localize "OSE.Delete"}}'><i
class="fas fa-trash"></i></a>
{{/if}}
</div>
</div>
</li>
{{/each}}
</div>
</ul>
{{/each}}
</ol>
</div>
</div>
{{!-- Saving throws --}}
<div class="attribute-group">