WIP: Party sheet

master
U~man 2020-07-14 16:37:13 +02:00
parent dcd2aee76e
commit 71f6c9fda3
11 changed files with 286 additions and 13 deletions

View File

@ -4,6 +4,7 @@
"OSE.Show": "Show",
"OSE.Add": "Add",
"OSE.Ok": "Ok",
"OSE.Update": "Update",
"OSE.Reset": "Reset",
"OSE.Cancel": "Cancel",
"OSE.Roll": "Roll",
@ -11,6 +12,9 @@
"OSE.Failure": "Failure",
"OSE.dialog.tweaks": "Tweaks",
"OSE.dialog.partysheet": "Party Overview",
"OSE.dialog.selectActors": "Select PCs",
"OSE.dialog.dealXP": "Deal XP",
"OSE.Formula": "Formula",
"OSE.SitMod": "Situational Modifier",
@ -137,6 +141,7 @@
"OSE.NPCReaction": "NPC Reaction",
"OSE.RetainersMax": "#Retainers",
"OSE.category.saves": "Saves",
"OSE.category.attributes": "Attributes",
"OSE.category.inventory": "Inventory",
"OSE.category.abilities": "Abilities",

View File

@ -11,6 +11,7 @@
"OSE.Failure": "Fallo",
"OSE.dialog.tweaks": "Ajustes",
"OSE.dialog.partysheet": "Party Sheet",
"OSE.Formula": "Formula",
"OSE.SitMod": "Mod. Situational",

View File

@ -11,6 +11,7 @@
"OSE.Failure": "Échec",
"OSE.dialog.tweaks": "Ajuster",
"OSE.dialog.partysheet": "Fiche de Groupe",
"OSE.Formula": "Formule",
"OSE.SitMod": "Mod. de situation",
@ -137,6 +138,7 @@
"OSE.NPCReaction": "Réaction",
"OSE.RetainersMax": "#Suivants",
"OSE.category.saves": "Sauvegardes",
"OSE.category.attributes": "Stats",
"OSE.category.inventory": "Inventaire",
"OSE.category.abilities": "Aptitudes",

View File

@ -19,6 +19,9 @@ export class OseCombat {
// Set init
for (let i = 0; i < data.combatants.length; ++i) {
if (!data.combatants[i].actor) {
return;
}
if (data.combatants[i].actor.data.data.isSlow) {
data.combatants[i].initiative = -789;
} else {
@ -118,6 +121,9 @@ export class OseCombat {
});
html.find('.combat-control[data-control="reroll"]').click((ev) => {
if (!game.combat) {
return;
}
let data = {};
OseCombat.rollInitiative(game.combat, data);
game.combat.update({ data: data });

View File

@ -0,0 +1,119 @@
export class OsePartySheet extends FormApplication {
static get defaultOptions() {
const options = super.defaultOptions;
(options.classes = ["ose", "dialog", "party-sheet"]),
(options.id = "party-sheet");
options.template = "systems/ose/templates/apps/party-sheet.html";
options.width = 700;
return options;
}
/* -------------------------------------------- */
/**
* Add the Entity name into the window title
* @type {String}
*/
get title() {
return game.i18n.localize("OSE.dialog.partysheet");
}
/* -------------------------------------------- */
/**
* Construct and return the data object used to render the HTML template for this form application.
* @return {Object}
*/
getData() {
let data = {
data: this.object,
config: CONFIG.OSE,
user: game.user
};
return data;
}
_onDrop(event) {
event.preventDefault();
console.log("DROPPING");
let data;
try {
data = JSON.parse(event.dataTransfer.getData("text/plain"));
if (data.type !== "Item") return;
} catch (err) {
return false;
}
console.log(data);
}
/* -------------------------------------------- */
_dealXP(ev) {
// Grab experience
const template = `
<form>
<div class="form-group">
<label>How much ?</label>
<input name="total" placeholder="0" type="text"/>
</div>
</form>`;
let pcs = this.object.entities.filter((e) => {
return e.data.type == "character";
});
new Dialog({
title: "Deal Experience",
content: template,
buttons: {
set: {
icon: '<i class="fas fa-hand"></i>',
label: game.i18n.localize("OSE.dialog.dealXP"),
callback: (html) => {
let toDeal = html.find('input[name="total"]').val();
const value = parseFloat(toDeal) / pcs.length;
if (value) {
// Give experience
pcs.forEach((t) => {
t.getExperience(Math.floor(value));
});
}
},
},
},
}).render(true);
}
async _selectActors(ev) {
const template = "/systems/ose/templates/apps/party-select.html";
const templateData = {
actors: this.object.entities
}
const content = await renderTemplate(template, templateData);
new Dialog({
title: "Select Party Characters",
content: content,
buttons: {
set: {
icon: '<i class="fas fa-save"></i>',
label: game.i18n.localize("OSE.Update"),
callback: (html) => {
let checks = html.find("input[data-action='select-actor']");
checks.each(async (_, c) => {
let key = c.getAttribute('name');
await this.object.entities[key].setFlag('ose', 'party', c.checked);
});
},
},
},
}).render(true);
}
/** @override */
activateListeners(html) {
super.activateListeners(html);
html
.find("button[data-action='select-actors']")
.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));
}
}

17
src/module/party.js Normal file
View File

@ -0,0 +1,17 @@
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>`;
html.find(".fas.fa-search").replaceWith($(control))
html.find('.ose-party-sheet').click(ev => {
showPartySheet(object);
})
}
export const showPartySheet = (object) => {
event.preventDefault();
new OsePartySheet(object, {
top: window.screen.height / 2,
left:window.screen.width / 2,
}).render(true);
}

View File

@ -11,6 +11,7 @@ import { registerHelpers } from "./module/helpers.js";
import * as chat from "./module/chat.js";
import * as treasure from "./module/treasure.js";
import * as macros from "./module/macros.js";
import * as party from "./module/party.js";
import { OseCombat } from "./module/combat.js";
/* -------------------------------------------- */
@ -83,6 +84,9 @@ Hooks.once("ready", async () => {
// License and KOFI infos
Hooks.on("renderSidebarTab", async (object, html) => {
if (object instanceof ActorDirectory) {
party.addControl(object, html);
}
if (object instanceof Settings) {
const template = "systems/ose/templates/chat/license.html";
const rendered = await renderTemplate(template);

View File

@ -5,6 +5,67 @@
}
}
.ose.dialog.party-sheet {
.window-content {
padding: 0;
height: 200px;
}
.header {
color: whitesmoke;
background: $darkBackground;
padding: 4px 0;
text-align: center;
}
.actor-list {
margin: 0;
overflow: auto;
height: 180px;
list-style: none;
padding: 0;
.actor {
&:nth-child(even) {
background-color: rgba(0, 0, 0, 0.1);
}
padding: 2px;
font-size: 12px;
height: 35px;
text-align: center;
line-height: 35px;
.field-img {
flex: 0 0 32px;
img {
border: none;
width: 32px;
height: 32px;
}
}
}
}
.field-name {
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;
text-align: center;
}
input {
width: calc(100% - 30px);
}
}
.ose.dialog.modifiers {
.attribute-bonuses {
label {

View File

@ -0,0 +1,12 @@
<form autocomplete="off" onsubmit="event.preventDefault();">
<ol class="actor-list">
{{#each actors as |actor key|}}
<li class="form-group actor" data-actor-id="{{actor.id}}">
<label>{{actor.name}}</label>
<div class="form-fields">
<input type="checkbox" data-action="select-actor" name="{{key}}" data-dtype="Boolean" {{checked actor.data.flags.ose.party}}/>
</div>
</li>
{{/each}}
</ol>
</form>

View File

@ -0,0 +1,59 @@
<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>
{{/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'}}
</div>
</div>
<ol class="actor-list">
{{#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}}"/>
</div>
<div class="field-name">
{{e.name}}
</div>
<div class="field-long">
{{e.data.data.hp.value}}/{{e.data.data.hp.max}}
</div>
<div class="field-short">
{{e.data.data.ac.value}}
</div>
<div class="field-short">
{{e.data.data.thac0.value}}
</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>
</li>
{{/if}}
{{/each}}
</ol>
</form>

View File

@ -1,13 +0,0 @@
<form autocomplete="off" onsubmit="event.preventDefault();">
<ol class="trait-list">
{{#each choices as |choice key|}}
<li>
<label class="checkbox">
<input type="checkbox" name="{{key}}" data-dtype="Boolean" {{checked choice.chosen}}>
{{localize choice.label}}
</label>
</li>
{{/each}}
</ol>
<button type="submit" name="submit" value="1"><i class="far fa-save"></i> Update Actor</button>
</form>