Add a PC list summary

This commit is contained in:
sladecraven 2022-11-25 20:47:28 +01:00
parent 7b4e5bcbfa
commit e1c7304551
11 changed files with 207 additions and 53 deletions

View File

@ -524,7 +524,12 @@ ul.no-bullets {
}
.items-list .item-name-fixed-medium {
min-width: 8rem;
width: 8rem;
width: 8rem;
}
.items-list .item-field-fixed-short {
max-width: 3rem;
min-width: 3rem;
width: 3rem;
}
/* ----------------------------------------- */
/* Premade colors */
@ -1063,3 +1068,6 @@ body.system-bol img#logo {
.character-summary-container {
  opacity: 0.95;
}
.character-summary-rollable {
text-decoration: underline;
}

View File

@ -172,6 +172,16 @@
"BOL.ui.boleffects": "Effets (automatiques)",
"BOL.ui.modifier": "Modificateur",
"BOL.ui.effects": "Effets en cours",
"BOL.ui.pcname": "PJs",
"BOL.ui.npcname": "PNJs",
"BOL.ui.pclistbutton": "Vue compacte",
"BOL.ui.noactorfound": "PNJ inconnu, le PNJ doit être présent dans le monde pour s'afficher ici.",
"BOL.ui.deletetitle": "Suppression",
"BOL.ui.confirmdelete": "Vous êtes sûr de vouloir supprimer cet item ?",
"BOL.ui.nomorealchemypoints": "Plus assez de Points de Création !",
"BOL.ui.armornoformula": "L'armure {protect.name} n'a pas de formule pour la protection !",
"BOL.ui.selectactor": "Selectionnez votre personnage pour utiliser la macro",
"BOL.ui.itemnotfound": "Impossible de trouver l'objet de cette macro",
"BOL.featureCategory.origins": "Origines",
"BOL.featureCategory.races": "Races",

View File

@ -94,8 +94,8 @@ export class BoLActorSheet extends ActorSheet {
// Delete Inventory Item
html.find('.item-delete').click(ev => {
Dialog.confirm({
title: "Suppression",
content: `Vous êtes sûr de vouloir supprimer cet item ?`,
title: game.i18n.localize("BOL.ui.deletetitle"),
content: game.i18n.localize("BOL.ui.confirmdelete"),
yes: () => {
const li = $(ev.currentTarget).parents(".item");
this.actor.deleteEmbeddedDocuments("Item", [li.data("itemId")])

View File

@ -343,7 +343,7 @@ export class BoLActor extends Actor {
getDamageAttributeValue(attrDamage) {
let attrDamageValue = 0
if (attrDamage.includes("vigor")) {
attrDamageValue = actor.system.attributes.vigor.value
attrDamageValue = this.system.attributes.vigor.value
if (attrDamage.includes("half")) {
attrDamageValue = Math.floor(attrDamageValue / 2)
}
@ -405,7 +405,7 @@ export class BoLActor extends Actor {
newPC = alchemy.system.properties.pccurrent + pcCost
await this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'system.properties.pccurrent': newPC }])
} else {
ui.notifications.warn("Plus assez de Points de Création !")
ui.notifications.warn( game.i18n.localize("BOL.ui.nomorealchemypoints") )
}
}
}
@ -638,13 +638,13 @@ export class BoLActor extends Actor {
} else if (protect.system.subtype == 'armor') {
if (BoLUtility.getRollArmor()) {
if (!protect.system.properties.soak.formula || protect.system.properties.soak.formula == "") {
ui.notifications.warn(`L'armure ${protect.name} n'a pas de formule pour la protection !`)
ui.notifications.warn( game.i18n.localize("BOL.ui.armornoformula", protect.name) )
} else {
formula += "+" + " max(" + protect.system.properties.soak.formula + ",0)"
}
} else {
if (protect.system.properties.soak.value == undefined) {
ui.notifications.warn(`L'armure ${protect.name} n'a pas de valeur fixe pour la protection !`)
ui.notifications.warn( game.i18n.localize("BOL.ui.armornoformula", protect.name) )
} else {
formula += "+ " + protect.system.properties.soak.value
}

View File

@ -16,6 +16,7 @@ import { BoLTokenHud } from "./system/bol-action-hud.js"
import { BoLHotbar } from "./system/bol-hotbar.js"
import { BoLAdventureGenerator } from "./system/bol-adventure-generator.js"
import { BoLCommands} from "./system/bol-commands.js"
import { BoLCharacterSummary} from "./system/bol-character-summary.js"
/* -------------------------------------------- */
Hooks.once('init', async function () {
@ -117,7 +118,10 @@ function welcomeMessage() {
/* -------------------------------------------- */
Hooks.once('ready', async function () {
BoLUtility.ready()
BoLCharacterSummary.ready()
registerUsageCount('bol')

View File

@ -7,22 +7,28 @@ export class BoLCharacterSummary extends Application {
/* -------------------------------------------- */
static displayPCSummary(){
let pcList = new BoLCharacterSummary()
pcList.render(true)
game.bol.charSummary.render(true)
}
/* -------------------------------------------- */
static createSummaryPos() {
return { top: 200, left: 200 };
}
/* -------------------------------------------- */
static ready() {
let charSummary = new BoLCharacterSummary()
game.bol.charSummary = charSummary
}
/* -------------------------------------------- */
constructor() {
if ( !game.user.isGM ) { // Uniquement si GM
return;
}
super();
//game.settings.set("world", "character-summary-data", {npcList: [], x:0, y:0})
this.settings = game.settings.get("world", "character-summary-data")
}
/* -------------------------------------------- */
@ -31,6 +37,7 @@ export class BoLCharacterSummary extends Application {
template: "systems/bol/templates/apps/character-summary-template.html",
popOut: true,
resizable: true,
dragDrop: [{ dragSelector: ".items-list .item", dropSelector: null }],
classes: ["bol", "dialog"], width: 820, height: 'fit-content'
})
}
@ -40,16 +47,62 @@ export class BoLCharacterSummary extends Application {
let formData = super.getData();
formData.pcs = game.actors.filter( ac => ac.type == "character" && ac.hasPlayerOwner )
formData.npcs = []
let newList = []
let toUpdate = false
for( let actorId of this.settings.npcList ) {
let actor = game.actors.get(actorId)
if (actor) {
formData.npcs.push( actor )
newList.push(actorId)
} else {
toUpdate = true
}
}
formData.config = game.bol.config
if ( toUpdate ) {
this.settings.npcList = newList
//console.log("Going to update ...", this.settings)
game.settings.set("world", "character-summary-data", this.settings)
}
return formData
}
/* -------------------------------------------- */
updateNPC() {
game.settings.set("world", "character-summary-data", game.bol.charSummary.settings)
game.bol.charSummary.close()
setTimeout( function() { game.bol.charSummary.render(true)}, 500)
}
/* -------------------------------------------- */
async _onDrop(event) {
//console.log("Dragged data are : ", dragData)
let data = event.dataTransfer.getData('text/plain')
let dataItem = JSON.parse( data)
let actor = fromUuidSync(dataItem.uuid)
if (actor) {
game.bol.charSummary.settings.npcList.push( actor.id )
game.bol.charSummary.updateNPC()
} else {
ui.notifications.warn( game.i18n.localize("BOL.ui.noactorfound") )
}
}
/* -------------------------------------------- */
/** @override */
async activateListeners(html) {
super.activateListeners(html);
html.find('.actor-open').click((event) => {
const li = $(event.currentTarget).parents(".item")
const actor = game.actors.get(li.data("actor-id"))
actor.sheet.render(true)
})
html.find('.summary-roll').click((event) => {
const li = $(event.currentTarget).parents(".item")
const actor = game.actors.get(li.data("actor-id"))
@ -61,6 +114,15 @@ export class BoLCharacterSummary extends Application {
BoLRoll.aptitudeCheck(actor, key, event)
}
})
html.find('.actor-delete').click(event => {
const li = $(event.currentTarget).parents(".item");
let actorId = li.data("actor-id")
let newList = game.bol.charSummary.settings.npcList.filter(id => id != actorId)
game.bol.charSummary.settings.npcList = newList
game.bol.charSummary.updateNPC()
})
}
}

View File

@ -48,12 +48,12 @@ export class BoLHotbar {
if (speaker.token) actor = game.actors.tokens[speaker.token]
if (!actor) actor = game.actors.get(speaker.actor)
if (!actor) {
return ui.notifications.warn(`Selectionnez votre personnage pour utiliser la macro`)
return ui.notifications.warn( game.i18n.localize("BOL.ui.selectactor") )
}
let item = actor.items.find(it => it.name === itemName && it.type == itemType)
if (!item ) {
return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`)
return ui.notifications.warn( game.i18n.localize("BOL.ui.itemnotfound") )
}
// Trigger the item roll
if (item.system.category === "equipment" && item.system.subtype === "weapon") {

View File

@ -28,6 +28,13 @@ export class BoLUtility {
type: Boolean,
onChange: lang => window.location.reload()
})
game.settings.register("world", "character-summary-data", {
name: "character-summary-data",
scope: "world",
config: false,
default: { npcList : [], x: 200, y: 200},
type: Object
})
game.settings.register("bol", "logoActorSheet", {
name: "Chemin du logo des fiches de perso",
hint: "Vous pouvez changer le logo BoL des fiches de perso, pour jouer dans un autre univers (idéalement 346 x 200, défaut : /systems/bol/ui/logo.webp)",
@ -69,11 +76,12 @@ export class BoLUtility {
static getLogoTopLeft() {
return this.logoTopLeft
}
/* -------------------------------------------- */
static async ready() {
//$("#logo").attr("src", this.getLogoTopLeft() )
$("#logo").css("content",`url(${this.getLogoTopLeft()})`)
$("#logo").css("content", `url(${this.getLogoTopLeft()})`)
}
/* -------------------------------------------- */
@ -271,7 +279,7 @@ export class BoLUtility {
let attackId = event.currentTarget.attributes['data-attack-id'].value
let defenseMode = event.currentTarget.attributes['data-defense-mode'].value
let weaponId = (event.currentTarget.attributes['data-weapon-id']) ? event.currentTarget.attributes['data-weapon-id'].value : -1
// Remove message for all
let msgId = BoLUtility.findChatMessageId(event.currentTarget)
if (game.user.isGM) {
@ -287,7 +295,7 @@ export class BoLUtility {
if (!game.user.isGM) {
return
}
BoLUtility.removeChatMessageId( msgId )
BoLUtility.removeChatMessageId(msgId)
console.log("Damage Handling", attackId, defenseMode, weaponId)
// Only GM process this
let attackDef = this.attackStore[attackId]
@ -334,10 +342,10 @@ export class BoLUtility {
let defenderUser
for (let user of game.users) {
if ( user.character && user.character.id == defender.id ) {
if (user.character && user.character.id == defender.id) {
defenderUser = user
}
}
}
let damageResults = {
attackId: attackDef.id,
attacker: attackDef.attacker,
@ -534,31 +542,29 @@ export class BoLUtility {
}
/* -------------------------------------------- */
static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id");
let msgTxt = "<p>Are you sure to remove this Item ?";
let buttons = {
delete: {
icon: '<i class="fas fa-check"></i>',
label: "Yes, remove it",
callback: () => {
actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]);
li.slideUp(200, () => actorSheet.render(false));
}
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Cancel"
}
}
msgTxt += "</p>";
let d = new Dialog({
title: "Confirm removal",
content: msgTxt,
buttons: buttons,
default: "cancel"
});
d.render(true);
static async loadCompendiumData(compendium) {
const pack = game.packs.get(compendium);
return await pack?.getDocuments() ?? [];
}
/* -------------------------------------------- */
static async loadCompendium(compendium, filter = item => true) {
let compendiumData = await this.loadCompendiumData(compendium);
return compendiumData.filter(filter);
}
/* -------------------------------------------- */
static async searchItem(dataItem) {
let item
if (dataItem.pack) {
let id = dataItem.id || dataItem._id
let items = await this.loadCompendium(dataItem.pack, item => item.id == id)
item = items[0] || undefined
} else {
item = game.items.get(dataItem.id)
}
return item
}
}

View File

@ -75,4 +75,18 @@ export default function registerHooks() {
}
return false;
});
/********************************************************************************** */
Hooks.on("renderActorDirectory", (app, html, data) => {
if (game.user.isGM) {
const button = document.createElement('button');
button.style.width = '95%';
button.innerHTML = game.i18n.localize("BOL.ui.pclistbutton")
button.addEventListener('click', () => {
game.bol.charSummary.render(true)
})
html.find('.header-actions').after(button)
}
})
}

View File

@ -14,7 +14,7 @@
],
"url": "https://www.uberwald.me/gitea/public/bol",
"license": "LICENSE.txt",
"version": "10.4.3",
"version": "10.4.4",
"compatibility": {
"minimum": "10",
"verified": "10",
@ -203,7 +203,7 @@
],
"socket": true,
"manifest": "https://www.uberwald.me/gitea/public/bol/raw/v10/system.json",
"download": "https://www.uberwald.me/gitea/public/bol/archive/bol-v10.4.3.zip",
"download": "https://www.uberwald.me/gitea/public/bol/archive/bol-v10.4.4.zip",
"background": "systems/images/map_lemurie.webp",
"gridDistance": 1.5,
"gridUnits": "m",

View File

@ -3,30 +3,80 @@
<ol class="items-list">
<li class="item flexrow item-header">
<div class="item-field item-name item-name-fixed-medium">{{localize "BOL.ui.name"}}</div>
<div class="item-field item-name item-name-fixed-medium">{{localize "BOL.ui.pcname"}}</div>
{{#each config.attackAttributes as |attr key|}}
<div class="item-field flex2">{{abbrev3 (localize attr)}}</div>
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize attr)}}</div>
{{/each}}
{{#each config.aptitudes as |apt key|}}
<div class="item-field flex2">{{abbrev3 (localize apt)}}</div>
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize apt)}}</div>
{{/each}}
{{#each config.resources as |res key|}}
<div class="item-field flex2">{{abbrev3 (localize res)}}</div>
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize res)}}</div>
{{/each}}
<div class="item-field flex1 right">
</div>
</li>
{{#each pcs as |pc key|}}
<li class="item flexrow" data-actor-id="{{pc.id}}">
<div class="item-field item-name item-name-fixed-medium">{{pc.name}}</div>
<div class="item-field item-name item-name-fixed-medium">
<a class="actor-open character-summary-rollable">{{pc.name}}</a>
</div>
{{#each pc.system.attributes as |attr key|}}
<div class="item-field flex2 "><a class="summary-roll" data-type="attribute" data-key="{{key}}">{{attr.value}}</a></div>
<div class="item-field flex2 item-field-fixed-short">
<a class="summary-roll character-summary-rollable" data-type="attribute" data-key="{{key}}">{{attr.value}}</a>
</div>
{{/each}}
{{#each pc.system.aptitudes as |apt key|}}
<div class="item-field flex2 "><a class="summary-roll" data-type="aptitude" data-key="{{key}}">{{apt.value}}</a></div>
<div class="item-field flex item-field-fixed-short">
<a class="summary-roll character-summary-rollable" data-type="aptitude" data-key="{{key}}">{{apt.value}}</a>
</div>
{{/each}}
{{#each pc.system.resources as |res key|}}
<div class="item-field flex2 ">{{res.value}}/{{res.max}}</div>
<div class="item-field flex2 item-field-fixed-short">{{res.value}}/{{res.max}}</div>
{{/each}}
<div class="item-field flex1 right">
</div>
</li>
{{/each}}
<li class="item flexrow item-header">
<div class="item-field item-name item-name-fixed-medium">{{localize "BOL.ui.npcname"}}</div>
{{#each config.attackAttributes as |attr key|}}
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize attr)}}</div>
{{/each}}
{{#each config.aptitudes as |apt key|}}
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize apt)}}</div>
{{/each}}
{{#each config.resources as |res key|}}
<div class="item-field flex2 item-field-fixed-short">{{abbrev3 (localize res)}}</div>
{{/each}}
<div class="item-field flex1 right">
</div>
</li>
{{#each npcs as |pc key|}}
<li class="item flexrow" data-actor-id="{{pc.id}}">
<div class="item-field item-name item-name-fixed-medium">
<a class="actor-open character-summary-rollable">{{pc.name}}</a>
</div>
{{#each pc.system.attributes as |attr key|}}
<div class="item-field flex2 item-field-fixed-short">
<a class="summary-roll character-summary-rollable" data-type="attribute" data-key="{{key}}">{{attr.value}}</a>
</div>
{{/each}}
{{#each pc.system.aptitudes as |apt key|}}
<div class="item-field flex item-field-fixed-short">
<a class="summary-roll character-summary-rollable" data-type="aptitude" data-key="{{key}}">{{apt.value}}</a>
</div>
{{/each}}
{{#each pc.system.resources as |res key|}}
<div class="item-field flex2 item-field-fixed-short">{{res.value}}/{{res.max}}</div>
{{/each}}
<div class="item-field flex1 right">
<a class="item-control actor-delete" title="{{localize "BOL.ui.delete"}}"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}