ENH: Party sheet v2
parent
a14bd9fd42
commit
853225545d
|
@ -47,89 +47,11 @@ export class OseActorSheetCharacter extends OseActorSheet {
|
|||
);
|
||||
data.config.ascendingAC = game.settings.get("ose", "ascendingAC");
|
||||
data.config.individualInit = game.settings.get("ose", "individualInit");
|
||||
|
||||
// Compute treasure
|
||||
let total = 0;
|
||||
data.owned.items.forEach((item) => {
|
||||
if (item.data.treasure) {
|
||||
total += item.data.quantity.value * item.data.cost;
|
||||
}
|
||||
});
|
||||
data.treasure = total;
|
||||
|
||||
data.config.encumbrance = game.settings.get("ose", "encumbranceOption");
|
||||
let basic = data.config.encumbrance == "basic";
|
||||
// Compute encumbrance
|
||||
let totalWeight = 0;
|
||||
Object.values(data.owned).forEach((cat) => {
|
||||
cat.forEach((item) => {
|
||||
if (item.type == "item" && (!basic || item.data.treasure)) {
|
||||
totalWeight += item.data.quantity.value * item.data.weight;
|
||||
} else if (!basic) {
|
||||
totalWeight += item.data.weight;
|
||||
}
|
||||
});
|
||||
});
|
||||
data.encumbrance = {
|
||||
pct: Math.clamped(
|
||||
(100 * parseFloat(totalWeight)) / data.data.encumbrance.max,
|
||||
0,
|
||||
100
|
||||
),
|
||||
max: data.data.encumbrance.max,
|
||||
encumbered: totalWeight > data.data.encumbrance.max,
|
||||
value: totalWeight,
|
||||
};
|
||||
|
||||
if (data.data.config.movementAuto) {
|
||||
this._calculateMovement(data, totalWeight);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
_calculateMovement(data, weight) {
|
||||
if (data.config.encumbrance == "disabled") return;
|
||||
let delta = data.encumbrance.max - 1600;
|
||||
if (data.config.encumbrance == "detailed") {
|
||||
if (weight > data.encumbrance.max) {
|
||||
data.data.movement.base = 0;
|
||||
} else if (weight > 800 + delta) {
|
||||
data.data.movement.base = 30;
|
||||
} else if (weight > 600 + delta) {
|
||||
data.data.movement.base = 60;
|
||||
} else if (weight > 400 + delta) {
|
||||
data.data.movement.base = 90;
|
||||
} else {
|
||||
data.data.movement.base = 120;
|
||||
}
|
||||
} else if (data.config.encumbrance == "basic") {
|
||||
let heaviest = 0;
|
||||
data.owned.armors.forEach((a) => {
|
||||
if (a.data.equipped) {
|
||||
if (a.data.type == "light" && heaviest == 0) {
|
||||
heaviest = 1;
|
||||
} else if (a.data.type == "heavy") {
|
||||
heaviest = 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
switch (heaviest) {
|
||||
case 0:
|
||||
data.data.movement.base = 120;
|
||||
break;
|
||||
case 1:
|
||||
data.data.movement.base = 90;
|
||||
break;
|
||||
case 2:
|
||||
data.data.movement.base = 60;
|
||||
break;
|
||||
}
|
||||
if (weight > game.settings.get("ose", "significantTreasure")) {
|
||||
data.data.movement.base -= 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _chooseLang() {
|
||||
let choices = CONFIG.OSE.languages;
|
||||
|
|
|
@ -13,6 +13,8 @@ export class OseActor extends Actor {
|
|||
this.computeModifiers();
|
||||
this._isSlow();
|
||||
this.computeAC();
|
||||
this.computeEncumbrance();
|
||||
this.computeTreasure();
|
||||
|
||||
// Determine Initiative
|
||||
if (game.settings.get("ose", "individualInit")) {
|
||||
|
@ -24,7 +26,6 @@ export class OseActor extends Actor {
|
|||
data.initiative.value = 0;
|
||||
}
|
||||
data.movement.encounter = data.movement.base / 3;
|
||||
|
||||
}
|
||||
/* -------------------------------------------- */
|
||||
/* Socket Listeners and Handlers
|
||||
|
@ -216,8 +217,8 @@ export class OseActor extends Actor {
|
|||
data: data,
|
||||
skipDialog: skip,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: game.i18n.format("OSE.roll.attribute", {attribute: label}),
|
||||
title: game.i18n.format("OSE.roll.attribute", {attribute: label}),
|
||||
flavor: game.i18n.format("OSE.roll.attribute", { attribute: label }),
|
||||
title: game.i18n.format("OSE.roll.attribute", { attribute: label }),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -272,8 +273,8 @@ export class OseActor extends Actor {
|
|||
data: data,
|
||||
skipDialog: true,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: game.i18n.localize('OSE.roll.appearing'),
|
||||
title: game.i18n.localize('OSE.roll.appearing'),
|
||||
flavor: game.i18n.localize("OSE.roll.appearing"),
|
||||
title: game.i18n.localize("OSE.roll.appearing"),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -303,8 +304,8 @@ export class OseActor extends Actor {
|
|||
data: data,
|
||||
skipDialog: skip,
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: game.i18n.format("OSE.roll.exploration", {exploration: label}),
|
||||
title: game.i18n.format("OSE.roll.exploration", {exploration: label}),
|
||||
flavor: game.i18n.format("OSE.roll.exploration", { exploration: label }),
|
||||
title: game.i18n.format("OSE.roll.exploration", { exploration: label }),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -353,7 +354,7 @@ export class OseActor extends Actor {
|
|||
const data = this.data.data;
|
||||
const rollParts = ["1d20"];
|
||||
const dmgParts = [];
|
||||
let label = game.i18n.format('OSE.roll.attacks', {name: this.data.name})
|
||||
let label = game.i18n.format("OSE.roll.attacks", { name: this.data.name });
|
||||
if (
|
||||
!attData.dmg ||
|
||||
(!game.settings.get("ose", "variableWeaponDamage") &&
|
||||
|
@ -361,7 +362,7 @@ export class OseActor extends Actor {
|
|||
) {
|
||||
dmgParts.push("1d6");
|
||||
} else {
|
||||
label = game.i18n.format('OSE.roll.attacksWith', {name: attData.label})
|
||||
label = game.i18n.format("OSE.roll.attacksWith", { name: attData.label });
|
||||
dmgParts.push(attData.dmg);
|
||||
}
|
||||
|
||||
|
@ -450,6 +451,101 @@ export class OseActor extends Actor {
|
|||
});
|
||||
}
|
||||
|
||||
computeEncumbrance() {
|
||||
if (this.data.type != "character") {
|
||||
return;
|
||||
}
|
||||
const data = this.data.data;
|
||||
let option = game.settings.get("ose", "encumbranceOption");
|
||||
let basic = option == "basic";
|
||||
|
||||
// Compute encumbrance
|
||||
let owned = ["weapon", "armor", "item"];
|
||||
let totalWeight = 0;
|
||||
Object.values(this.data.items).forEach((item) => {
|
||||
if (item.type == "item" && (!basic || item.data.treasure)) {
|
||||
totalWeight += item.data.quantity.value * item.data.weight;
|
||||
} else if (!basic && owned.includes(item.type)) {
|
||||
totalWeight += item.data.weight;
|
||||
}
|
||||
});
|
||||
|
||||
data.encumbrance = {
|
||||
pct: Math.clamped(
|
||||
(100 * parseFloat(totalWeight)) / data.encumbrance.max,
|
||||
0,
|
||||
100
|
||||
),
|
||||
max: data.encumbrance.max,
|
||||
encumbered: totalWeight > data.encumbrance.max,
|
||||
value: totalWeight,
|
||||
};
|
||||
|
||||
if (data.config.movementAuto && option != 'disabled') {
|
||||
this._calculateMovement();
|
||||
}
|
||||
}
|
||||
|
||||
_calculateMovement() {
|
||||
const data = this.data.data;
|
||||
let option = game.settings.get("ose", "encumbranceOption");
|
||||
let weight = data.encumbrance.value;
|
||||
let delta = data.encumbrance.max - 1600;
|
||||
if (option == "detailed") {
|
||||
if (weight > data.encumbrance.max) {
|
||||
data.movement.base = 0;
|
||||
} else if (weight > 800 + delta) {
|
||||
data.movement.base = 30;
|
||||
} else if (weight > 600 + delta) {
|
||||
data.movement.base = 60;
|
||||
} else if (weight > 400 + delta) {
|
||||
data.movement.base = 90;
|
||||
} else {
|
||||
data.movement.base = 120;
|
||||
}
|
||||
} else if (option == "basic") {
|
||||
const armors = this.data.items.filter(i => i.type == "armor");
|
||||
let heaviest = 0;
|
||||
armors.forEach((a) => {
|
||||
if (a.data.equipped) {
|
||||
if (a.data.type == "light" && heaviest == 0) {
|
||||
heaviest = 1;
|
||||
} else if (a.data.type == "heavy") {
|
||||
heaviest = 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
switch (heaviest) {
|
||||
case 0:
|
||||
data.movement.base = 120;
|
||||
break;
|
||||
case 1:
|
||||
data.movement.base = 90;
|
||||
break;
|
||||
case 2:
|
||||
data.movement.base = 60;
|
||||
break;
|
||||
}
|
||||
if (weight > game.settings.get("ose", "significantTreasure")) {
|
||||
data.movement.base -= 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
computeTreasure() {
|
||||
if (this.data.type != "character") {
|
||||
return;
|
||||
}
|
||||
const data = this.data.data;
|
||||
// Compute treasure
|
||||
let total = 0;
|
||||
let treasure = this.data.items.filter(i => (i.type == "item" && i.data.treasure))
|
||||
treasure.forEach((item) => {
|
||||
total += item.data.quantity.value * item.data.cost;
|
||||
});
|
||||
data.treasure = total;
|
||||
}
|
||||
|
||||
computeAC() {
|
||||
if (this.data.type != "character") {
|
||||
return;
|
||||
|
@ -461,7 +557,7 @@ export class OseActor extends Actor {
|
|||
const data = this.data.data;
|
||||
data.aac.naked = baseAc + data.scores.dex.mod;
|
||||
data.ac.naked = baseAc - data.scores.dex.mod;
|
||||
const armors = this.data.items.filter(i => i.type == 'armor');
|
||||
const armors = this.data.items.filter((i) => i.type == "armor");
|
||||
armors.forEach((a) => {
|
||||
if (a.data.equipped && a.data.type != "shield") {
|
||||
baseAc = a.data.ac.value;
|
||||
|
|
|
@ -4,7 +4,7 @@ export class OsePartySheet extends FormApplication {
|
|||
(options.classes = ["ose", "dialog", "party-sheet"]),
|
||||
(options.id = "party-sheet");
|
||||
options.template = "systems/ose/templates/apps/party-sheet.html";
|
||||
options.width = 700;
|
||||
options.width = 280;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,14 @@ export class OsePartySheet extends FormApplication {
|
|||
* @return {Object}
|
||||
*/
|
||||
getData() {
|
||||
const settings = {
|
||||
ascending: game.settings.get('ose', 'ascendingAC')
|
||||
};
|
||||
let data = {
|
||||
data: this.object,
|
||||
config: CONFIG.OSE,
|
||||
user: game.user
|
||||
user: game.user,
|
||||
settings: settings
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
@ -115,5 +119,9 @@ export class OsePartySheet extends FormApplication {
|
|||
.click(this._selectActors.bind(this));
|
||||
html.find("button[data-action='deal-xp']").click(this._dealXP.bind(this));
|
||||
html.find("a.resync").click(() => this.render(true));
|
||||
html.find(".field-img").click((ev) => {
|
||||
let actorId = ev.currentTarget.parentElement.dataset.actorId;
|
||||
game.actors.get(actorId).sheet.render(true);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { OsePartySheet } from "./dialog/party-sheet.js";
|
||||
|
||||
export const addControl = (object, html) => {
|
||||
let control = `<a class='ose-party-sheet' title='${game.i18n.localize('OSE.dialog.partysheet')}'><i class='fas fa-users'></i></a>`;
|
||||
let control = `<button class='ose-party-sheet' type="button" title='${game.i18n.localize('OSE.dialog.partysheet')}'><i class='fas fa-users'></i></button>`;
|
||||
html.find(".fas.fa-search").replaceWith($(control))
|
||||
html.find('.ose-party-sheet').click(ev => {
|
||||
showPartySheet(object);
|
||||
|
@ -11,7 +11,17 @@ export const addControl = (object, html) => {
|
|||
export const showPartySheet = (object) => {
|
||||
event.preventDefault();
|
||||
new OsePartySheet(object, {
|
||||
top: window.screen.height / 2,
|
||||
left:window.screen.width / 2,
|
||||
top: window.screen.height / 2 - 180,
|
||||
left:window.screen.width / 2 - 140,
|
||||
}).render(true);
|
||||
}
|
||||
|
||||
export const update = (actor, data) => {
|
||||
if (actor.getFlag('ose', 'party')) {
|
||||
Object.values(ui.windows).forEach(w => {
|
||||
if (w instanceof OsePartySheet) {
|
||||
w.render(true);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ export const registerSettings = function () {
|
|||
basic: "OSE.Setting.EncumbranceBasic",
|
||||
detailed: "OSE.Setting.EncumbranceDetailed",
|
||||
},
|
||||
onChange: _ => window.location.reload()
|
||||
});
|
||||
|
||||
game.settings.register("ose", "significantTreasure", {
|
||||
|
@ -58,5 +59,6 @@ export const registerSettings = function () {
|
|||
scope: "world",
|
||||
type: Number,
|
||||
config: true,
|
||||
onChange: _ => window.location.reload()
|
||||
});
|
||||
};
|
||||
|
|
|
@ -124,5 +124,5 @@ Hooks.on("preUpdateCombat", async (combat, data, diff, id) => {
|
|||
Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));
|
||||
Hooks.on("getChatLogEntryContext", chat.addChatMessageContextOptions);
|
||||
Hooks.on("renderChatMessage", chat.addChatMessageButtons);
|
||||
|
||||
Hooks.on("renderRollTableConfig", treasure.augmentTable);
|
||||
Hooks.on("renderRollTableConfig", treasure.augmentTable);
|
||||
Hooks.on("updateActor", party.update);
|
|
@ -8,7 +8,7 @@
|
|||
.ose.dialog.party-sheet {
|
||||
.window-content {
|
||||
padding: 0;
|
||||
height: 200px;
|
||||
height: 400px;
|
||||
}
|
||||
.header {
|
||||
color: whitesmoke;
|
||||
|
@ -19,24 +19,25 @@
|
|||
.actor-list {
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
height: 180px;
|
||||
height: 350px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
.actor {
|
||||
&:nth-child(even) {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
padding: 2px;
|
||||
.fas {
|
||||
padding: 0 2px;
|
||||
}
|
||||
padding: 4px;
|
||||
font-size: 12px;
|
||||
height: 35px;
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
.field-img {
|
||||
flex: 0 0 32px;
|
||||
flex: 0 0 50px;
|
||||
img {
|
||||
border: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,24 +46,16 @@
|
|||
text-align: left;
|
||||
text-indent: 10px;
|
||||
}
|
||||
.field-short {
|
||||
flex: 0 0 45px;
|
||||
}
|
||||
.field-long {
|
||||
flex: 0 0 80px;
|
||||
}
|
||||
.field-longer {
|
||||
flex: 0 0 180px;
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar #actors .directory-header .header-search {
|
||||
.ose-party-sheet {
|
||||
width: 30px;
|
||||
width: 32px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
}
|
||||
input {
|
||||
width: calc(100% - 30px);
|
||||
width: calc(100% - 45px);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
.modifiers-btn {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -5px;
|
||||
top: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
<li class="item-titles flexrow">
|
||||
<div class="item-caret"><i class="fas fa-caret-down"></i></div>
|
||||
<div class="item-name">{{localize "OSE.items.Treasure"}}</div>
|
||||
<div class="field-long">{{treasure}} <i class="fas fa-circle"></i></div>
|
||||
<div class="field-long">{{data.treasure}} <i class="fas fa-circle"></i></div>
|
||||
<div class="field-short"><i class="fas fa-hashtag"></i></div>
|
||||
<div class="field-short"><i class="fas fa-weight-hanging"></i></div>
|
||||
<div class="item-controls">
|
||||
|
@ -189,7 +189,7 @@
|
|||
</div>
|
||||
</section>
|
||||
<section>
|
||||
{{#with encumbrance}}
|
||||
{{#with data.encumbrance}}
|
||||
<div class="encumbrance {{#if encumbered}}encumbered{{/if}}">
|
||||
<span class="encumbrance-bar" style="width:{{pct}}%"></span>
|
||||
<span class="encumbrance-label">{{value}} / {{max}}</span>
|
||||
|
|
|
@ -1,62 +1,71 @@
|
|||
<form autocomplete="off">
|
||||
<header class="flexrow">
|
||||
{{#if user.isGM}}
|
||||
<button data-action="select-actors" type="button">{{localize "OSE.dialog.selectActors"}}</button>
|
||||
<button data-action="deal-xp" type="button">{{localize "OSE.dialog.dealXP"}}</button>
|
||||
<button data-action="select-actors" type="button">
|
||||
{{localize "OSE.dialog.selectActors"}}
|
||||
</button>
|
||||
<button data-action="deal-xp" type="button">
|
||||
{{localize "OSE.dialog.dealXP"}}
|
||||
</button>
|
||||
{{/if}}
|
||||
</header>
|
||||
<div class="actor header flexrow">
|
||||
<div class="field-name">
|
||||
<a class="resync"><i class="fas fa-sync"></i></a>
|
||||
</div>
|
||||
<div class="field-long">
|
||||
{{localize 'OSE.Health'}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
{{localize 'OSE.ArmorClassShort'}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
{{localize 'OSE.Thac0'}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
{{localize 'OSE.movement.encounter.short'}}
|
||||
</div>
|
||||
<div class="field-longer">
|
||||
{{localize 'OSE.category.saves'}}
|
||||
<a class="resync"><i class="fas fa-sync"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="actor-list">
|
||||
{{#each data.entities as |e|}}
|
||||
{{#if e.data.flags.ose.party}}
|
||||
{{#each data.entities as |e|}} {{#if e.data.flags.ose.party}}
|
||||
<li class="actor flexrow" data-actor-id="{{e.id}}">
|
||||
<div class="field-img">
|
||||
<img src="{{e.img}}"/>
|
||||
<a><img src="{{e.img}}" /></a>
|
||||
</div>
|
||||
<div class="field-name">
|
||||
{{e.name}}
|
||||
</div>
|
||||
<div class="item-controls field-long">
|
||||
<div class="item-control reaction-roll" title="Reaction Roll"><a><i class="fas fa-user"></i></a></div>
|
||||
</div>
|
||||
<div class="field-long">
|
||||
{{e.data.data.hp.value}}/{{e.data.data.hp.max}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
<strong>{{e.data.data.ac.value}}</strong> <sub>{{e.data.data.ac.naked}}</sub>
|
||||
</div>
|
||||
<div class="field-short">
|
||||
<sub>{{e.data.data.thac0.mod.melee}}</sub> <strong>{{e.data.data.thac0.value}}</strong> <sub>{{e.data.data.thac0.mod.missile}}</sub>
|
||||
</div>
|
||||
<div class="field-short">
|
||||
{{e.data.data.movement.encounter}}
|
||||
</div>
|
||||
<div class="field-longer flexrow">
|
||||
{{#each e.data.data.saves as |s i|}}
|
||||
<span>{{lookup @root.config.saves_short i}} {{s.value}}</span>
|
||||
{{/each}}
|
||||
<div>
|
||||
<div class="flexrow">
|
||||
<div class="field-name flex2">
|
||||
<strong>{{e.name}}</strong>
|
||||
</div>
|
||||
<div class="field-long">
|
||||
<i class="fas fa-heart"></i>
|
||||
{{e.data.data.hp.value}}/{{e.data.data.hp.max}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
{{#if @root.settings.ascending}}<strong>{{e.data.data.aac.value}}</strong> <sub>{{e.data.data.aac.naked}}</sub>
|
||||
{{else}}<strong>{{e.data.data.ac.value}}</strong> <sub>{{e.data.data.ac.naked}}</sub>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flexrow">
|
||||
<div class="field-short">
|
||||
<i class="fas fa-fist-raised"></i>
|
||||
<sub>{{e.data.data.thac0.mod.melee}}</sub> <strong>{{e.data.data.thac0.value}}</strong> <sub>{{e.data.data.thac0.mod.missile}}</sub>
|
||||
</div>
|
||||
<div class="field-short">
|
||||
<i class="fas fa-shoe-prints"></i>
|
||||
{{e.data.data.movement.encounter}}
|
||||
</div>
|
||||
{{#if (eq e.data.type 'character')}}
|
||||
<div class="field-short">
|
||||
<i class="fas fa-weight-hanging"></i>
|
||||
{{e.data.data.encumbrance.value}}
|
||||
</div>
|
||||
<div class="field-short">
|
||||
<i class="fas fa-circle"></i>
|
||||
{{e.data.data.treasure}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="flexrow">
|
||||
<div class="field-longer flexrow">
|
||||
{{#each e.data.data.saves as |s i|}}
|
||||
<span>{{lookup @root.config.saves_short i}} {{s.value}}</span>
|
||||
{{/each}}
|
||||
{{#if (eq e.data.type 'character')}}<i class="fas fa-magic"></i>{{mod e.data.data.scores.wis.mod}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{/if}} {{/each}}
|
||||
</ol>
|
||||
</form>
|
Loading…
Reference in New Issue